triangle_mesh.template.cc
Go to the documentation of this file.
1// LIC// ====================================================================
2// LIC// This file forms part of oomph-lib, the object-oriented,
3// LIC// multi-physics finite-element library, available
4// LIC// at http://www.oomph-lib.org.
5// LIC//
6// LIC// Copyright (C) 2006-2021 Matthias Heil and Andrew Hazel
7// LIC//
8// LIC// This library is free software; you can redistribute it and/or
9// LIC// modify it under the terms of the GNU Lesser General Public
10// LIC// License as published by the Free Software Foundation; either
11// LIC// version 2.1 of the License, or (at your option) any later version.
12// LIC//
13// LIC// This library is distributed in the hope that it will be useful,
14// LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// LIC// Lesser General Public License for more details.
17// LIC//
18// LIC// You should have received a copy of the GNU Lesser General Public
19// LIC// License along with this library; if not, write to the Free Software
20// LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21// LIC// 02110-1301 USA.
22// LIC//
23// LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24// LIC//
25// LIC//====================================================================
26#ifndef OOMPH_TRIANGLE_MESH_TEMPLATE_CC
27#define OOMPH_TRIANGLE_MESH_TEMPLATE_CC
28
29#include <iostream>
30
32#include "../generic/map_matrix.h"
33#include "../generic/multi_domain.h"
34#include "../generic/projection.h"
35#include "../generic/face_element_as_geometric_object.h"
36
37namespace oomph
38{
39 //======================================================================
40 /// Build with the help of the scaffold mesh coming
41 /// from the triangle mesh generator Triangle.
42 //======================================================================
43 template<class ELEMENT>
45 const bool& use_attributes)
46 {
47 // Mesh can only be built with 2D Telements.
48 MeshChecker::assert_geometric_element<TElementGeometricBase, ELEMENT>(2);
49
50 // Create space for elements
51 unsigned nelem = Tmp_mesh_pt->nelement();
52 Element_pt.resize(nelem);
53
54 // Create space for nodes
55 unsigned nnode_scaffold = Tmp_mesh_pt->nnode();
56
57 // Create a map storing the node_id of the mesh used to update the
58 // node position in the update_triangulateio function
59 std::map<Node*, unsigned> old_global_number;
60
61 // Store the TriangulateIO node id
62 for (unsigned inod = 0; inod < nnode_scaffold; inod++)
63 {
64 Node* old_node_pt = Tmp_mesh_pt->node_pt(inod);
65 old_global_number[old_node_pt] = inod;
66 }
67
68 // Initialize the old node id vector
69 Oomph_vertex_nodes_id.resize(nnode_scaffold);
70
71 // Create space for nodes
72 Node_pt.resize(nnode_scaffold, 0);
73
74 // Set number of boundaries
75 unsigned nbound = Tmp_mesh_pt->nboundary();
76
77 // Resize the boundary information
78 set_nboundary(nbound);
79 Boundary_element_pt.resize(nbound);
80 Face_index_at_boundary.resize(nbound);
81
82 // If we have different regions, then resize the region
83 // information
84 if (use_attributes)
85 {
86 Boundary_region_element_pt.resize(nbound);
87 Face_index_region_at_boundary.resize(nbound);
88 }
89
90 // Loop over elements in scaffold mesh, visit their nodes
91 for (unsigned e = 0; e < nelem; e++)
92 {
93 Element_pt[e] = new ELEMENT;
94 }
95
96 // Number of nodes per element from the scaffold mesh
97 unsigned nnod_el = Tmp_mesh_pt->finite_element_pt(0)->nnode();
98
99 // Setup map to check the (pseudo-)global node number
100 // Nodes whose number is zero haven't been copied across
101 // into the mesh yet.
102 std::map<Node*, unsigned> global_number;
103 unsigned global_count = 0;
104
105 // Map of Element attribute pairs
106 std::map<double, Vector<FiniteElement*>> element_attribute_map;
107
108 // If we're using attributes
109 if (use_attributes)
110 {
111 // If we're using attributes then we need attribute 0 which will
112 // be associated with region 0
113 element_attribute_map[0].resize(0);
114 }
115
116 // Loop over elements in scaffold mesh, visit their nodes
117 for (unsigned e = 0; e < nelem; e++)
118 {
119 // Loop over all nodes in element
120 for (unsigned j = 0; j < nnod_el; j++)
121 {
122 // Pointer to node in the scaffold mesh
123 Node* scaffold_node_pt = Tmp_mesh_pt->finite_element_pt(e)->node_pt(j);
124
125 // Get the (pseudo-)global node number in scaffold mesh
126 // (It's zero [=default] if not visited this one yet)
127 unsigned j_global = global_number[scaffold_node_pt];
128
129 // Haven't done this one yet
130 if (j_global == 0)
131 {
132 // Find and store the node_id in the old nodes map
133 Oomph_vertex_nodes_id[global_count] =
134 old_global_number[scaffold_node_pt];
135
136 // Get pointer to set of mesh boundaries that this
137 // scaffold node occupies; NULL if the node is not on any boundary
138 std::set<unsigned>* boundaries_pt;
139 scaffold_node_pt->get_boundaries_pt(boundaries_pt);
140
141 // Storage for the new node
142 Node* new_node_pt = 0;
143
144 // Is it on boundaries
145 if (boundaries_pt != 0)
146 {
147 // Create new boundary node
148 new_node_pt =
149 finite_element_pt(e)->construct_boundary_node(j, time_stepper_pt);
150
151 // Add to boundaries
152 for (std::set<unsigned>::iterator it = boundaries_pt->begin();
153 it != boundaries_pt->end();
154 ++it)
155 {
156 add_boundary_node(*it, new_node_pt);
157 }
158 }
159 // Build normal node
160 else
161 {
162 // Create new normal node
163 new_node_pt =
164 finite_element_pt(e)->construct_node(j, time_stepper_pt);
165 }
166
167 // Give it a number (not necessarily the global node
168 // number in the scaffold mesh -- we just need something
169 // to keep track...)
170 global_count++;
171 global_number[scaffold_node_pt] = global_count;
172
173 // Copy new node, created using the NEW element's construct_node
174 // function into global storage, using the same global
175 // node number that we've just associated with the
176 // corresponding node in the scaffold mesh
177 Node_pt[global_count - 1] = new_node_pt;
178
179 // Assign coordinates
180 for (unsigned i = 0; i < finite_element_pt(e)->dim(); i++)
181 {
182 new_node_pt->x(i) = scaffold_node_pt->x(i);
183 }
184 }
185 // This one has already been done: Copy accross
186 else
187 {
188 finite_element_pt(e)->node_pt(j) = Node_pt[j_global - 1];
189 }
190 }
191
192 // If we're using attributes
193 if (use_attributes)
194 {
195 element_attribute_map[Tmp_mesh_pt->element_attribute(e)].push_back(
196 finite_element_pt(e));
197 }
198 }
199
200 // Now let's construct lists
201 // Find the number of attributes
202 if (use_attributes)
203 {
204 unsigned n_attribute = element_attribute_map.size();
205
206 // There are n_attribute different regions
207 this->Region_attribute.resize(n_attribute);
208
209 // Copy the vectors in the map over to our internal storage
210 unsigned count = 0;
211 for (std::map<double, Vector<FiniteElement*>>::iterator it =
212 element_attribute_map.begin();
213 it != element_attribute_map.end();
214 ++it)
215 {
216 this->Region_attribute[count] = it->first;
217 Region_element_pt[static_cast<unsigned>(Region_attribute[count])] =
218 it->second;
219 ++count;
220 }
221 }
222
223 // At this point we've created all the elements and
224 // created their vertex nodes. Now we need to create
225 // the additional (midside and internal) nodes!
226
227 unsigned boundary_id = 0;
228
229 // Get number of nodes along element edge and dimension of element (2)
230 // from first element
231 unsigned n_node_1d = finite_element_pt(0)->nnode_1d();
232 unsigned dim = finite_element_pt(0)->dim();
233
234 // Storage for the local coordinate of the new node
235 Vector<double> s(dim);
236
237 // Get number of nodes in the element from first element
238 unsigned n_node = finite_element_pt(0)->nnode();
239
240 // Storage for each global edge of the mesh
241 unsigned n_global_edge = Tmp_mesh_pt->nglobal_edge();
242 Vector<Vector<Node*>> nodes_on_global_edge(n_global_edge);
243
244 // Loop over elements
245 for (unsigned e = 0; e < nelem; e++)
246 {
247 // Cache pointers to the elements
248 FiniteElement* const elem_pt = finite_element_pt(e);
249 FiniteElement* const tmp_elem_pt = Tmp_mesh_pt->finite_element_pt(e);
250
251 // The number of edge nodes is 3*(nnode_1d-1)
252 unsigned n_edge_node = 3 * (n_node_1d - 1);
253
254 // If there are any more nodes, these are internal and can be
255 // constructed and added directly to the mesh
256 for (unsigned n = n_edge_node; n < n_node; ++n)
257 {
258 // Create new node (it can never be a boundary node)
259 Node* new_node_pt = elem_pt->construct_node(n, time_stepper_pt);
260
261 // What are the node's local coordinates?
262 elem_pt->local_coordinate_of_node(n, s);
263
264 // Find the coordinates of the new node from the existing
265 // and fully-functional element in the scaffold mesh
266 for (unsigned i = 0; i < dim; i++)
267 {
268 new_node_pt->x(i) = tmp_elem_pt->interpolated_x(s, i);
269 }
270
271 // Add the node to the mesh's global look-up scheme
272 Node_pt.push_back(new_node_pt);
273 }
274
275 // Now loop over the mid-side edge nodes
276 // Start from node number 3
277 unsigned n = 3;
278
279 // Loop over edges
280 for (unsigned j = 0; j < 3; j++)
281 {
282 // Find the boundary id of the edge
283 boundary_id = Tmp_mesh_pt->edge_boundary(e, j);
284
285 // Find the global edge index
286 unsigned edge_index = Tmp_mesh_pt->edge_index(e, j);
287
288 // If the nodes on the edge have not been allocated, construct them
289 if (nodes_on_global_edge[edge_index].size() == 0)
290 {
291 // Loop over the nodes on the edge excluding the ends
292 for (unsigned j2 = 0; j2 < n_node_1d - 2; ++j2)
293 {
294 // Storage for the new node
295 Node* new_node_pt = 0;
296
297 // If the edge is on a boundary, construct a boundary node
298 if (boundary_id > 0)
299 {
300 new_node_pt =
301 elem_pt->construct_boundary_node(n, time_stepper_pt);
302 // Add it to the boundary
303 this->add_boundary_node(boundary_id - 1, new_node_pt);
304 }
305 // Otherwise construct a normal node
306 else
307 {
308 new_node_pt = elem_pt->construct_node(n, time_stepper_pt);
309 }
310
311 // What are the node's local coordinates?
312 elem_pt->local_coordinate_of_node(n, s);
313
314 // Find the coordinates of the new node from the existing
315 // and fully-functional element in the scaffold mesh
316 for (unsigned i = 0; i < dim; i++)
317 {
318 new_node_pt->x(i) = tmp_elem_pt->interpolated_x(s, i);
319 }
320
321 // Add to the global node list
322 Node_pt.push_back(new_node_pt);
323
324 // Add to the edge index
325 nodes_on_global_edge[edge_index].push_back(new_node_pt);
326 // Increment the node number
327 ++n;
328 }
329 }
330 // Otherwise just set the pointers
331 // using the fact that the next time the edge is visited
332 // the nodes must be arranged in the other order because all
333 // triangles have the same orientation
334 else
335 {
336 // Loop over the nodes on the edge excluding the ends
337 for (unsigned j2 = 0; j2 < n_node_1d - 2; ++j2)
338 {
339 // Set the local node from the edge but indexed the other
340 // way around
341 elem_pt->node_pt(n) =
342 nodes_on_global_edge[edge_index][n_node_1d - 3 - j2];
343 ++n;
344 }
345 }
346
347 // Set the elements adjacent to the boundary from the
348 // boundary id information
349 if (boundary_id > 0)
350 {
351 Boundary_element_pt[boundary_id - 1].push_back(elem_pt);
352 // Need to put a shift in here because of an inconsistent naming
353 // convention between triangle and face elements
354 Face_index_at_boundary[boundary_id - 1].push_back((j + 2) % 3);
355
356 // If using regions set up the boundary information
357 if (use_attributes)
358 {
359 unsigned tmp_region =
360 static_cast<unsigned>(Tmp_mesh_pt->element_attribute(e));
361 // Element adjacent to boundary
362 Boundary_region_element_pt[boundary_id - 1][tmp_region].push_back(
363 elem_pt);
364 // Need to put a shift in here because of an inconsistent naming
365 // convention between triangle and face elements
366 Face_index_region_at_boundary[boundary_id - 1][tmp_region]
367 .push_back((j + 2) % 3);
368 }
369 }
370
371 } // end of loop over edges
372 } // end of loop over elements
373
374
375 // Lookup scheme has now been setup
376 Lookup_for_elements_next_boundary_is_setup = true;
377 }
378
379#ifdef OOMPH_HAS_MPI
380
381 //======================================================================
382 /// Identify the segments from the old mesh (original mesh)
383 /// in the new mesh (this) and assign initial and final boundary
384 /// coordinates for the segments that create the boundary
385 //======================================================================
386 template<class ELEMENT>
389 const unsigned& b, TriangleMesh<ELEMENT>* original_mesh_pt)
390 {
391 // ------------------------------------------------------------------
392 // First: Get the face elements associated with the current boundary
393 // (nonhalo elements only)
394 // ------------------------------------------------------------------
395 // Temporary storage for face elements
396 Vector<FiniteElement*> face_el_pt;
397
398 // Temporary storage for number of elements adjacent to the boundary
399 unsigned nele = 0;
400
401 // Temporary storage for elements adjacent to the boundary that have
402 // a common edge (related with internal boundaries)
403 unsigned n_repeated_ele = 0;
404
405 const unsigned n_regions = this->nregion();
406
407 // map to associate the face element to the bulk element, necessary
408 // to attach halo face elements at both sides of each found segment
409 std::map<FiniteElement*, FiniteElement*> face_to_bulk_element_pt;
410
411 // Temporary storage for already done nodes
412 Vector<std::pair<Node*, Node*>> done_nodes_pt;
413
414 // If there is more than one region then only use boundary
415 // coordinates from the bulk side (region 0)
416 if (n_regions > 1)
417 {
418 for (unsigned rr = 0; rr < n_regions; rr++)
419 {
420 const unsigned region_id =
421 static_cast<unsigned>(this->Region_attribute[rr]);
422
423 // Loop over all elements on boundaries in region i_r
424 const unsigned nel_in_region =
425 this->nboundary_element_in_region(b, region_id);
426
427 unsigned nel_repetead_in_region = 0;
428
429 // Only bother to do anything else, if there are elements
430 // associated with the boundary and the current region
431 if (nel_in_region > 0)
432 {
433 // Flag that activates when a repeated face element is found,
434 // possibly because we are dealing with an internal boundary
435 bool repeated = false;
436
437 // Loop over the bulk elements adjacent to boundary b
438 for (unsigned e = 0; e < nel_in_region; e++)
439 {
440 // Get pointer to the bulk element that is adjacent to boundary b
441 FiniteElement* bulk_elem_pt =
442 this->boundary_element_in_region_pt(b, region_id, e);
443
444#ifdef OOMPH_HAS_MPI
445 // In a distributed mesh only work with nonhalo elements
446 if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
447 {
448 // Increase the number of repeated elements
449 n_repeated_ele++;
450 // Go for the next element
451 continue;
452 }
453#endif
454
455 // Find the index of the face of element e along boundary b
456 int face_index =
457 this->face_index_at_boundary_in_region(b, region_id, e);
458
459 // Before adding the new element we need to be sure that
460 // the edge that this element represent has not been
461 // already added
462 FiniteElement* tmp_ele_pt =
463 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
464
465 const unsigned n_nodes = tmp_ele_pt->nnode();
466
467 std::pair<Node*, Node*> tmp_pair = std::make_pair(
468 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
469
470 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
471 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
472
473 // Search for repeated nodes
474 const unsigned n_done_nodes = done_nodes_pt.size();
475 for (unsigned l = 0; l < n_done_nodes; l++)
476 {
477 if (tmp_pair == done_nodes_pt[l] ||
478 tmp_pair_inverse == done_nodes_pt[l])
479 {
480 nel_repetead_in_region++;
481 repeated = true;
482 break;
483 }
484 }
485
486 // Create new face element
487 if (!repeated)
488 {
489 // Add the pair of nodes (edge) to the node dones
490 done_nodes_pt.push_back(tmp_pair);
491 // Create the map to know if the element is halo
492 face_el_pt.push_back(tmp_ele_pt);
493 // Add the element to the face elements
494 face_to_bulk_element_pt[tmp_ele_pt] = bulk_elem_pt;
495 }
496 else
497 {
498 // Clean up
499 delete tmp_ele_pt;
500 tmp_ele_pt = 0;
501 }
502
503 // Re-start
504 repeated = false;
505
506 } // for (e < nel_in_region)
507
508 nele += nel_in_region;
509
510 n_repeated_ele += nel_repetead_in_region;
511
512 } // if (nel_in_region > 0)
513 } // for (rr < n_regions)
514 } // if (n_regions > 1)
515 // Otherwise it's just the normal boundary functions
516 else
517 {
518 // Loop over all elements on boundaries
519 nele = this->nboundary_element(b);
520
521 // Only bother to do anything else, if there are elements
522 if (nele > 0)
523 {
524 // Flag that activates when a repeated face element is found,
525 // possibly because we are dealing with an internal boundary
526 bool repeated = false;
527
528 // Loop over the bulk elements adjacent to boundary b
529 for (unsigned e = 0; e < nele; e++)
530 {
531 // Get pointer to the bulk element that is adjacent to boundary b
532 FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
533
534#ifdef OOMPH_HAS_MPI
535 // In a distributed mesh only work with nonhalo elements
536 if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
537 {
538 // Increase the number of repeated elements
539 n_repeated_ele++;
540 // Go for the next element
541 continue;
542 }
543#endif
544
545 // Find the index of the face of element e along boundary b
546 int face_index = this->face_index_at_boundary(b, e);
547
548 // Before adding the new element we need to be sure that
549 // the edge that this element represents has not been
550 // already added (only applies for internal boundaries)
551 FiniteElement* tmp_ele_pt =
552 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
553
554 const unsigned n_nodes = tmp_ele_pt->nnode();
555
556 std::pair<Node*, Node*> tmp_pair = std::make_pair(
557 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
558
559 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
560 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
561
562 // Search for repeated nodes
563 const unsigned n_done_nodes = done_nodes_pt.size();
564 for (unsigned l = 0; l < n_done_nodes; l++)
565 {
566 if (tmp_pair == done_nodes_pt[l] ||
567 tmp_pair_inverse == done_nodes_pt[l])
568 {
569 // Increase the number of repeated elements
570 n_repeated_ele++;
571 // Mark the element as repeated
572 repeated = true;
573 break;
574 }
575 }
576
577 // Create new face element
578 if (!repeated)
579 {
580 // Add the pair of nodes (edge) to the node dones
581 done_nodes_pt.push_back(tmp_pair);
582 // Add the element to the face elements
583 face_el_pt.push_back(tmp_ele_pt);
584 // Create the map to know if the element is halo
585 face_to_bulk_element_pt[tmp_ele_pt] = bulk_elem_pt;
586 }
587 else
588 {
589 // Free the repeated bulk element!!
590 delete tmp_ele_pt;
591 tmp_ele_pt = 0;
592 }
593
594 // Re-start
595 repeated = false;
596
597 } // for (e < nel)
598 } // if (nel > 0)
599
600 } // else (n_regions > 1)
601
602 // Do not consider the repeated elements
603 nele -= n_repeated_ele;
604
605#ifdef PARANOID
606 if (nele != face_el_pt.size())
607 {
608 std::ostringstream error_message;
609 error_message
610 << "The independent counting of face elements (" << nele << ") for "
611 << "boundary (" << b << ") is different\n"
612 << "from the real number of face elements in the container ("
613 << face_el_pt.size() << ")\n";
614 throw OomphLibError(error_message.str(),
615 "TriangleMesh::identify_boundary_segments_and_assign_"
616 "initial_zeta_values()",
617 OOMPH_EXCEPTION_LOCATION);
618 }
619#endif
620
621 // Continue even thought there are no elements, the processor needs
622 // to participate in the communications
623
624 // ----------------------------------------------------------------
625 // Second: Sort the face elements, only consider nonhalo elements
626 // ----------------------------------------------------------------
627
628 // A flag vector to mark those face elements that are considered as
629 // halo in the current processor
630 std::vector<bool> is_halo_face_element(nele, false);
631
632 // Count the total number of non halo face elements
633 unsigned nnon_halo_face_elements = 0;
634
635 // We will have halo face elements if the mesh is distributed
636 for (unsigned ie = 0; ie < nele; ie++)
637 {
638 // Get the face element
639 FiniteElement* face_ele_pt = face_el_pt[ie];
640 // Get the bulk element
641 FiniteElement* tmp_bulk_ele_pt = face_to_bulk_element_pt[face_ele_pt];
642 // Check if the bulk element is halo
643 if (!tmp_bulk_ele_pt->is_halo())
644 {
645 is_halo_face_element[ie] = false;
646 nnon_halo_face_elements++;
647 }
648 else
649 {
650 // Mark the face element as halo
651 is_halo_face_element[ie] = true;
652 }
653 } // for (ie < nele)
654
655#ifdef PARANOID
656 // Get the total number of halo face elements
657 const unsigned nhalo_face_element = nele - nnon_halo_face_elements;
658 if (nhalo_face_element > 0)
659 {
660 std::ostringstream error_message;
661 error_message
662 << "There should not be halo face elements since they were not "
663 << "considered when computing the face elements\n\n"
664 << "The number of found halo face elements is: " << nhalo_face_element
665 << "\n\n";
666 throw OomphLibError(error_message.str(),
667 "TriangleMesh::identify_boundary_segments_and_assign_"
668 "initial_zeta_values()",
669 OOMPH_EXCEPTION_LOCATION);
670 }
671#endif
672
673 // The vector of list to store the "segments" that compound the
674 // boundary (segments may appear only in a distributed mesh)
675 Vector<std::list<FiniteElement*>> segment_sorted_ele_pt;
676
677 // Number of already sorted face elements (only nonhalo elements for
678 // a distributed mesh)
679 unsigned nsorted_face_elements = 0;
680
681 // Keep track of who's done (this apply to nonhalo only, remember we
682 // are only working with nonhalo elements)
683 std::map<FiniteElement*, bool> done_el;
684
685 // Keep track of which element is inverted (in distributed mesh the
686 // elements may be inverted with respect to the segment they belong)
687 std::map<FiniteElement*, bool> is_inverted;
688
689 // Iterate until all possible segments have been created
690 while (nsorted_face_elements < nnon_halo_face_elements)
691 {
692 // The ordered list of face elements (in a distributed mesh a
693 // collection of contiguous face elements define a segment)
694 std::list<FiniteElement*> sorted_el_pt;
695 sorted_el_pt.clear();
696
697#ifdef PARANOID
698 // Select an initial element for the segment
699 bool found_initial_face_element = false;
700#endif
701
702 FiniteElement* ele_face_pt = 0;
703
704 unsigned iface = 0;
705 for (iface = 0; iface < nele; iface++)
706 {
707 if (!is_halo_face_element[iface])
708 {
709 ele_face_pt = face_el_pt[iface];
710 // If not done then take it as initial face element
711 if (!done_el[ele_face_pt])
712 {
713#ifdef PARANOID
714 found_initial_face_element = true;
715#endif
716 nsorted_face_elements++;
717 iface++; // The next element number
718 sorted_el_pt.push_back(ele_face_pt);
719 // Mark as done
720 done_el[ele_face_pt] = true;
721 break;
722 }
723 }
724 } // for (iface < nele)
725
726#ifdef PARANOID
727 if (!found_initial_face_element)
728 {
729 std::ostringstream error_message;
730 error_message
731 << "Could not find an initial face element for the current segment\n";
732 throw OomphLibError(error_message.str(),
733 "TriangleMesh::identify_boundary_segments_and_"
734 "assign_initial_zeta_values()",
735 OOMPH_EXCEPTION_LOCATION);
736 }
737#endif
738
739 // Number of nodes
740 const unsigned nnod = ele_face_pt->nnode();
741
742 // Left and right most nodes (the left and right nodes of the
743 // current face element)
744 Node* left_node_pt = ele_face_pt->node_pt(0);
745 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
746
747 // Continue iterating if a new face element has been added to the
748 // list
749 bool face_element_added = false;
750
751 // While a new face element has been added to the set of sorted
752 // face elements then re-iterate
753 do
754 {
755 // Start from the next face element since we have already added
756 // the previous one as the initial face element (any previous
757 // face element had to be added on previous iterations)
758 for (unsigned iiface = iface; iiface < nele; iiface++)
759 {
760 // Re-start flag
761 face_element_added = false;
762
763 // Get the candidate element
764 ele_face_pt = face_el_pt[iiface];
765
766 // Check that the candidate element has not been done and is
767 // not a halo element
768 if (!(done_el[ele_face_pt] || is_halo_face_element[iiface]))
769 {
770 // Get the left and right nodes of the current element
771 Node* local_left_node_pt = ele_face_pt->node_pt(0);
772 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
773 // New element fits at the left of segment and is not inverted
774 if (left_node_pt == local_right_node_pt)
775 {
776 left_node_pt = local_left_node_pt;
777 sorted_el_pt.push_front(ele_face_pt);
778 is_inverted[ele_face_pt] = false;
779 face_element_added = true;
780 }
781 // New element fits at the left of segment and is inverted
782 else if (left_node_pt == local_left_node_pt)
783 {
784 left_node_pt = local_right_node_pt;
785 sorted_el_pt.push_front(ele_face_pt);
786 is_inverted[ele_face_pt] = true;
787 face_element_added = true;
788 }
789 // New element fits on the right of segment and is not inverted
790 else if (right_node_pt == local_left_node_pt)
791 {
792 right_node_pt = local_right_node_pt;
793 sorted_el_pt.push_back(ele_face_pt);
794 is_inverted[ele_face_pt] = false;
795 face_element_added = true;
796 }
797 // New element fits on the right of segment and is inverted
798 else if (right_node_pt == local_right_node_pt)
799 {
800 right_node_pt = local_left_node_pt;
801 sorted_el_pt.push_back(ele_face_pt);
802 is_inverted[ele_face_pt] = true;
803 face_element_added = true;
804 }
805
806 if (face_element_added)
807 {
808 done_el[ele_face_pt] = true;
809 nsorted_face_elements++;
810 break;
811 }
812
813 } // if (!(done_el[ele_face_pt] || is_halo_face_element[iiface]))
814 } // for (iiface<nnon_halo_face_element)
815 } while (face_element_added &&
816 (nsorted_face_elements < nnon_halo_face_elements));
817
818 // Store the created segment in the vector of segments
819 segment_sorted_ele_pt.push_back(sorted_el_pt);
820
821 } // while(nsorted_face_elements < nnon_halo_face_elements);
822
823 // The number of segments in this processor
824 const unsigned nsegments = segment_sorted_ele_pt.size();
825
826 // ------------------------------------------------------------------
827 // Third: We have the face elements sorted (nonhalo only), now
828 // assign boundary coordinates to the nodes in the segments. This is
829 // the LOCAL boundary coordinate which is required if the zeta
830 // values need to be inverted
831 // ------------------------------------------------------------------
832 // Necessary in case boundaries with no geom object associated need
833 // to be inverted the zeta values (It is necessary to compute the
834 // arclength but also to store the nodes in a container (set))
835 // ------------------------------------------------------------------
836
837 // Vector of sets that stores the nodes of each segment based on a
838 // lexicographically order starting from the bottom left node of
839 // each segment
840 Vector<std::set<Node*>> segment_all_nodes_pt;
841
842 // The arclength of each segment in the current processor
843 Vector<double> segment_arclength(nsegments);
844
845 // The number of vertices of each segment
846 Vector<unsigned> nvertices_per_segment(nsegments);
847
848 // The initial zeta for the segment
849 Vector<double> initial_zeta_segment(nsegments);
850
851 // The final zeta for the segment
852 Vector<double> final_zeta_segment(nsegments);
853
854#ifdef PARANOID
855 if (nnon_halo_face_elements > 0 && nsegments == 0)
856 {
857 std::ostringstream error_message;
858 error_message
859 << "The number of segments is zero, but the number of nonhalo\n"
860 << "elements is: (" << nnon_halo_face_elements << ")\n";
861 throw OomphLibError(error_message.str(),
862 "TriangleMesh::identify_boundary_segments_and_assign_"
863 "initial_zeta_values()",
864 OOMPH_EXCEPTION_LOCATION);
865 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
866#endif
867
868 // Go through all the segments and compute the LOCAL boundary
869 // coordinates
870 for (unsigned is = 0; is < nsegments; is++)
871 {
872#ifdef PARANOID
873 if (segment_sorted_ele_pt[is].size() == 0)
874 {
875 std::ostringstream error_message;
876 error_message << "The (" << is << ")-th segment has no elements\n";
877 throw OomphLibError(error_message.str(),
878 "TriangleMesh::identify_boundary_segments_and_"
879 "assign_initial_zeta_values()",
880 OOMPH_EXCEPTION_LOCATION);
881 } // if (segment_sorted_ele_pt[is].size() == 0)
882#endif
883
884 // Get access to the first element on the segment
885 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
886
887 // Number of nodes
888 const unsigned nnod = first_ele_pt->nnode();
889
890 // Get the first node of the current segment
891 Node* first_node_pt = first_ele_pt->node_pt(0);
892 if (is_inverted[first_ele_pt])
893 {
894 first_node_pt = first_ele_pt->node_pt(nnod - 1);
895 }
896
897 // Get access to the last element on the segment
898 FiniteElement* last_ele_pt = segment_sorted_ele_pt[is].back();
899
900 // Get the last node of the current segment
901 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
902 if (is_inverted[last_ele_pt])
903 {
904 last_node_pt = last_ele_pt->node_pt(0);
905 }
906
907 // Coordinates of left node
908 double x_left = first_node_pt->x(0);
909 double y_left = first_node_pt->x(1);
910
911 // Initialise boundary coordinate (local boundary coordinate for
912 // boundaries with more than one segment)
913 Vector<double> zeta(1, 0.0);
914
915 // If the boundary has an associated GeomObject then it is not
916 // necessary to compute the arclength, only read the values from
917 // the nodes at the edges
918 if (this->boundary_geom_object_pt(b) != 0)
919 {
920 first_node_pt->get_coordinates_on_boundary(b, zeta);
921 initial_zeta_segment[is] = zeta[0];
922 last_node_pt->get_coordinates_on_boundary(b, zeta);
923 final_zeta_segment[is] = zeta[0];
924 }
925
926 // Lexicographically bottom left node
927 std::set<Node*> local_nodes_pt;
928 local_nodes_pt.insert(first_node_pt);
929
930 // Now loop over nodes in order
931 for (std::list<FiniteElement*>::iterator it =
932 segment_sorted_ele_pt[is].begin();
933 it != segment_sorted_ele_pt[is].end();
934 it++)
935 {
936 // Get element
937 FiniteElement* el_pt = *it;
938
939 // Start node and increment
940 unsigned k_nod = 1;
941 int nod_diff = 1;
942 if (is_inverted[el_pt])
943 {
944 k_nod = nnod - 2;
945 nod_diff = -1;
946 }
947
948 // Loop over nodes
949 for (unsigned j = 1; j < nnod; j++)
950 {
951 Node* nod_pt = el_pt->node_pt(k_nod);
952 k_nod += nod_diff;
953
954 // Coordinates of right node
955 double x_right = nod_pt->x(0);
956 double y_right = nod_pt->x(1);
957
958 // Increment boundary coordinate (the arclength)
959 zeta[0] += sqrt((x_right - x_left) * (x_right - x_left) +
960 (y_right - y_left) * (y_right - y_left));
961
962 // // When we have a GeomObject associated to the boundary we already
963 // // know the zeta values for the nodes, there is no need to compute
964 // // the arclength
965 // if (this->boundary_geom_object_pt(b)==0)
966 // {
967 // // Set boundary coordinate
968 // nod_pt->set_coordinates_on_boundary(b, zeta);
969 // }
970
971 // Increment reference coordinate
972 x_left = x_right;
973 y_left = y_right;
974
975 // Get lexicographically bottom left node but only
976 // use vertex nodes as candidates
977 local_nodes_pt.insert(nod_pt);
978 } // for (j < nnod)
979
980 } // iterator over the elements in the segment
981
982 // Store the arclength of the segment
983 segment_arclength[is] = zeta[0];
984
985 // Store the number of vertices in the segment
986 nvertices_per_segment[is] = local_nodes_pt.size();
987
988 // Add the nodes for the corresponding segment in the container
989 segment_all_nodes_pt.push_back(local_nodes_pt);
990
991 } // for (is < nsegments)
992
993 // Get the number of sets for nodes
994#ifdef PARANOID
995 if (segment_all_nodes_pt.size() != nsegments)
996 {
997 std::ostringstream error_message;
998 error_message << "The number of segments (" << nsegments
999 << ") and the number of "
1000 << "sets of nodes (" << segment_all_nodes_pt.size()
1001 << ") representing\n"
1002 << "the\nsegments is different!!!\n\n";
1003 throw OomphLibError(
1004 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
1005 }
1006#endif
1007
1008 // Store the initial arclength for each segment of boundary in the
1009 // current processor, initalise to zero in case we have a non
1010 // distributed boundary
1011 Vector<double> initial_segment_arclength(nsegments, 0.0);
1012
1013 // Associated the index of the current segment to the segment index
1014 // in the original mesh (input mesh)
1015 Vector<unsigned> current_segment_to_original_segment_index(nsegments);
1016
1017 // Each segment needs to know whether it has to be inverted or not
1018 // Store whether a segment needs to be inverted or not
1019 Vector<unsigned> segment_inverted(nsegments);
1020
1021 // -----------------------------------------------------------------
1022 // Fourth: Identify the segments with the ones in the original mesh
1023 // (has sense only in the adaptation process)
1024 // -----------------------------------------------------------------
1025
1026 // Now check if there are segments associated to this boundary
1027 if (nsegments > 0)
1028 {
1029#ifdef PARANOID
1030 // Double check that the same number of coordinates (nsegments)
1031 // have been established for the boundary
1032 const unsigned nsegments_initial_coordinates =
1033 original_mesh_pt->boundary_segment_initial_coordinate(b).size();
1034
1035 const unsigned nsegments_final_coordinates =
1036 original_mesh_pt->boundary_segment_final_coordinate(b).size();
1037
1038 if (nsegments_initial_coordinates != nsegments_final_coordinates)
1039 {
1040 std::stringstream error_message;
1041 error_message
1042 << "The number of segments that present initial coordinates "
1043 << nsegments_initial_coordinates << " is different from "
1044 << "the\nnumber of segments that present final coordinates "
1045 << nsegments_final_coordinates << "\n\n";
1046 throw OomphLibError(error_message.str(),
1047 OOMPH_CURRENT_FUNCTION,
1048 OOMPH_EXCEPTION_LOCATION);
1049 } // if (nsegments_initial_coordinates!=nsegments_final_coordinates)
1050
1051 // Also check that the number of segments found in the previous
1052 // mesh is the same as the number of segments found in this mesh
1053 if (nsegments_initial_coordinates != nsegments)
1054 {
1055 std::stringstream error_message;
1056 error_message << "Working with boundary (" << b
1057 << ").\n The number of initial and "
1058 << "final coordinates (" << nsegments_initial_coordinates
1059 << ") is different from\n"
1060 << "the number of found segments (" << nsegments
1061 << ").\n\n";
1062 throw OomphLibError(error_message.str(),
1063 OOMPH_CURRENT_FUNCTION,
1064 OOMPH_EXCEPTION_LOCATION);
1065 } // if (nsegments_initial_coordinates != nsegments)
1066#endif
1067
1068 // Create a backup for the data from the original mesh
1069 // Backup for the coordinates
1070 Vector<Vector<double>> original_mesh_segment_initial_coordinate(
1071 nsegments);
1072 Vector<Vector<double>> original_mesh_segment_final_coordinate(nsegments);
1073 // Backup for the zeta values
1074 Vector<double> original_mesh_segment_initial_zeta(nsegments);
1075 Vector<double> original_mesh_segment_final_zeta(nsegments);
1076 // Backup for the arclengths
1077 Vector<double> original_mesh_segment_initial_arclength(nsegments);
1078 Vector<double> original_mesh_segment_final_arclength(nsegments);
1079 // Do the backup
1080 for (unsigned is = 0; is < nsegments; is++)
1081 {
1082 original_mesh_segment_initial_coordinate[is].resize(2);
1083 original_mesh_segment_final_coordinate[is].resize(2);
1084 for (unsigned k = 0; k < 2; k++)
1085 {
1086 original_mesh_segment_initial_coordinate[is][k] =
1087 original_mesh_pt->boundary_segment_initial_coordinate(b)[is][k];
1088 original_mesh_segment_final_coordinate[is][k] =
1089 original_mesh_pt->boundary_segment_final_coordinate(b)[is][k];
1090 }
1091 // Check if the boudary has an associated GeomObject
1092 if (this->boundary_geom_object_pt(b) != 0)
1093 {
1094 original_mesh_segment_initial_zeta[is] =
1095 original_mesh_pt->boundary_segment_initial_zeta(b)[is];
1096 original_mesh_segment_final_zeta[is] =
1097 original_mesh_pt->boundary_segment_final_zeta(b)[is];
1098 }
1099 else
1100 {
1101 original_mesh_segment_initial_arclength[is] =
1102 original_mesh_pt->boundary_segment_initial_arclength(b)[is];
1103 original_mesh_segment_final_arclength[is] =
1104 original_mesh_pt->boundary_segment_final_arclength(b)[is];
1105 }
1106 } // for (is < nsegments)
1107
1108 // Clear all the storage
1109 Boundary_segment_inverted[b].clear();
1110 Boundary_segment_initial_coordinate[b].clear();
1111 Boundary_segment_final_coordinate[b].clear();
1112
1113 Boundary_segment_initial_zeta[b].clear();
1114 Boundary_segment_final_zeta[b].clear();
1115
1116 Boundary_segment_initial_arclength[b].clear();
1117 Boundary_segment_final_arclength[b].clear();
1118
1119 // Identify each segment in the processor with the ones created
1120 // by the original mesh
1121 // -----------------------------------------------------------------
1122 // Keep track of the already identified segments
1123 std::map<unsigned, bool> segment_done;
1124 for (unsigned is = 0; is < nsegments; is++)
1125 {
1126#ifdef PARANOID
1127 // Flag to know if the segment was identified
1128 bool found_original_segment = false;
1129#endif
1130
1131 // Get the initial and final coordinates of the current segment
1132 Vector<double> current_seg_initial_coord(2);
1133 Vector<double> current_seg_final_coord(2);
1134
1135 // Get access to the initial element on the segment
1136 FiniteElement* current_seg_initial_ele_pt =
1137 segment_sorted_ele_pt[is].front();
1138
1139 // Number of nodes
1140 const unsigned nnod = current_seg_initial_ele_pt->nnode();
1141
1142 // Get the first node of the current segment
1143 Node* current_seg_first_node_pt =
1144 current_seg_initial_ele_pt->node_pt(0);
1145 if (is_inverted[current_seg_initial_ele_pt])
1146 {
1147 current_seg_first_node_pt =
1148 current_seg_initial_ele_pt->node_pt(nnod - 1);
1149 }
1150
1151 // Get access to the last element on the segment
1152 FiniteElement* current_seg_last_ele_pt =
1153 segment_sorted_ele_pt[is].back();
1154
1155 // Get the last node of the current segment
1156 Node* current_seg_last_node_pt =
1157 current_seg_last_ele_pt->node_pt(nnod - 1);
1158 if (is_inverted[current_seg_last_ele_pt])
1159 {
1160 current_seg_last_node_pt = current_seg_last_ele_pt->node_pt(0);
1161 }
1162
1163 // Get the coordinates for the first and last seg node
1164 for (unsigned i = 0; i < 2; i++)
1165 {
1166 current_seg_initial_coord[i] = current_seg_first_node_pt->x(i);
1167 current_seg_final_coord[i] = current_seg_last_node_pt->x(i);
1168 }
1169
1170 // We have got the initial and final coordinates of the current
1171 // segment, compare those with the initial and final coordinates
1172 // of the original mesh segments to identify which segments is
1173 // which
1174 for (unsigned orig_s = 0; orig_s < nsegments; orig_s++)
1175 {
1176 if (!segment_done[orig_s])
1177 {
1178 // Get the coordinates to compare
1179 Vector<double> initial_coordinate =
1180 original_mesh_segment_initial_coordinate[orig_s];
1181 Vector<double> final_coordinate =
1182 original_mesh_segment_final_coordinate[orig_s];
1183
1184 // Compute the distance initial(current)-initial(original)
1185 // coordinates
1186 double dist =
1187 ((current_seg_initial_coord[0] - initial_coordinate[0]) *
1188 (current_seg_initial_coord[0] - initial_coordinate[0])) +
1189 ((current_seg_initial_coord[1] - initial_coordinate[1]) *
1190 (current_seg_initial_coord[1] - initial_coordinate[1]));
1191 dist = sqrt(dist);
1192
1193 // If the initial node is the same, check for the last node
1195 {
1196 // Compute the distance final(current)-final(original)
1197 // coordinates
1198 dist = ((current_seg_final_coord[0] - final_coordinate[0]) *
1199 (current_seg_final_coord[0] - final_coordinate[0])) +
1200 ((current_seg_final_coord[1] - final_coordinate[1]) *
1201 (current_seg_final_coord[1] - final_coordinate[1]));
1202 dist = sqrt(dist);
1203
1204 // The final node is the same, we have identified the
1205 // segments
1207 {
1208 // Store the index that relates the previous index with the
1209 // current one
1210 current_segment_to_original_segment_index[is] = orig_s;
1211
1212 // In this case the segment is not inverted
1213 Boundary_segment_inverted[b].push_back(0);
1214
1215 // Copy the initial and final coordinates for each segment
1216 Boundary_segment_initial_coordinate[b].push_back(
1217 initial_coordinate);
1218 Boundary_segment_final_coordinate[b].push_back(
1219 final_coordinate);
1220
1221 // Check if the boundary has an associated GeomObject
1222 if (this->boundary_geom_object_pt(b) != 0)
1223 {
1224 // Copy the initial zeta value for the segment
1225 Boundary_segment_initial_zeta[b].push_back(
1226 original_mesh_segment_initial_zeta[orig_s]);
1227 Boundary_segment_final_zeta[b].push_back(
1228 original_mesh_segment_final_zeta[orig_s]);
1229 }
1230 else
1231 {
1232 // Copy the initial and final arclength for each
1233 // segment
1234 Boundary_segment_initial_arclength[b].push_back(
1235 original_mesh_segment_initial_arclength[orig_s]);
1236 Boundary_segment_final_arclength[b].push_back(
1237 original_mesh_segment_final_arclength[orig_s]);
1238 }
1239 // Mark the segment as done
1240 segment_done[orig_s] = true;
1241#ifdef PARANOID
1242 found_original_segment = true;
1243#endif
1244 break;
1245 } // The final(current) node matched with the
1246 // final(original) node
1247 } // The initial(current) node matched with the
1248 // initial(original) node
1249 else
1250 {
1251 // Check the inverted case Compute the distance
1252 // initial(current)-final(original) coordinates
1253 double dist_inv =
1254 ((current_seg_initial_coord[0] - final_coordinate[0]) *
1255 (current_seg_initial_coord[0] - final_coordinate[0])) +
1256 ((current_seg_initial_coord[1] - final_coordinate[1]) *
1257 (current_seg_initial_coord[1] - final_coordinate[1]));
1258 dist_inv = sqrt(dist_inv);
1259
1260 // If the initial node is the same as the final node of
1261 // the segment, check for the last node
1262 if (dist_inv <
1264 {
1265 // Compute the distance final(current)-initial(original)
1266 // coordinates
1267 dist_inv =
1268 ((current_seg_final_coord[0] - initial_coordinate[0]) *
1269 (current_seg_final_coord[0] - initial_coordinate[0])) +
1270 ((current_seg_final_coord[1] - initial_coordinate[1]) *
1271 (current_seg_final_coord[1] - initial_coordinate[1]));
1272 dist_inv = sqrt(dist_inv);
1273
1274 // The final node is the same as the initial node, we
1275 // have identified the segments
1276 if (dist_inv <
1278 {
1279 // Store the index that related the previous index with the
1280 // current one
1281 current_segment_to_original_segment_index[is] = orig_s;
1282
1283 // In this case the segment is inverted
1284 Boundary_segment_inverted[b].push_back(1);
1285
1286 // Copy the initial and final coordinates for each segment
1287 Boundary_segment_initial_coordinate[b].push_back(
1288 initial_coordinate);
1289 Boundary_segment_final_coordinate[b].push_back(
1290 final_coordinate);
1291
1292 // Check that the boudary has an associated GeomObject
1293 if (this->boundary_geom_object_pt(b) != 0)
1294 {
1295 // Copy the initial zeta value for the segments
1296 Boundary_segment_initial_zeta[b].push_back(
1297 original_mesh_segment_initial_zeta[orig_s]);
1298 Boundary_segment_final_zeta[b].push_back(
1299 original_mesh_segment_final_zeta[orig_s]);
1300 }
1301 else
1302 {
1303 // Copy the initial and final arclength for each segment
1304 Boundary_segment_initial_arclength[b].push_back(
1305 original_mesh_segment_initial_arclength[orig_s]);
1306 Boundary_segment_final_arclength[b].push_back(
1307 original_mesh_segment_final_arclength[orig_s]);
1308 }
1309 // Mark the segment as done
1310 segment_done[orig_s] = true;
1311#ifdef PARANOID
1312 found_original_segment = true;
1313#endif
1314 break;
1315 } // The final(current) node matched with the
1316 // initial(original) node
1317 } // The initial(current) node matched with the
1318 // final(original) node
1319 } // else (the first(current) node did not matched with the
1320 // first(original) node. Else do the inverted case
1321
1322 } // (!segment_done[orig_s])
1323
1324 } // (orig_s < nsegments)
1325
1326#ifdef PARANOID
1327 if (!found_original_segment)
1328 {
1329 std::stringstream error_message;
1330 error_message
1331 << "The (" << is << ")-th segment on the current segment was not\n"
1332 << "found when trying to identify it with the original mesh's\n"
1333 << "segment coordinates\n";
1334 throw OomphLibError(error_message.str(),
1335 OOMPH_CURRENT_FUNCTION,
1336 OOMPH_EXCEPTION_LOCATION);
1337 } // if (!found_original_segment)
1338#endif
1339 } // for (is < nsegments)
1340
1341 } // if (nsegments > 0)
1342
1343 // -------------------------------------------------------------------
1344 // Fourth: The original mesh is different from the current mesh
1345 // (this). For boundaries with no geom object associated check if it
1346 // is required to reverse the zeta values. In order to reverse the
1347 // zeta values it is required to previously compute the arclength of
1348 // the segments and store the nodes in a container (set). NOTE that
1349 // the setup_boundary_coordinate() method is not called for
1350 // boundaries with NO GeomObject associated, so this is the LAST
1351 // CHANCE to do it
1352 // -------------------------------------------------------------------
1353 // The original mesh is the same as the current mesh (this). The
1354 // setup_boundary_method() will be called only for the boundaries
1355 // with NO GeomObject associated
1356 // -------------------------------------------------------------------
1357 if (this != original_mesh_pt)
1358 {
1359 // Get the boundary arclength
1360
1361 // Get the initial and final zeta values for the boundary
1362 // (arclength) from the original mesh
1363 Vector<double> first_node_zeta_coordinate =
1364 original_mesh_pt->boundary_initial_zeta_coordinate(b);
1365 Vector<double> last_node_zeta_coordinate =
1366 original_mesh_pt->boundary_final_zeta_coordinate(b);
1367
1368 // The boundary arclength is the maximum of the initial and final
1369 // zeta coordinate
1370 const double boundary_arclength =
1371 std::max(first_node_zeta_coordinate[0], last_node_zeta_coordinate[0]);
1372
1373 for (unsigned is = 0; is < nsegments; is++)
1374 {
1375 // Here check if need to invert the elements and the boundary
1376 // coordinates for the segments in a boundary with no GeomObject
1377 // associated
1378 if (boundary_geom_object_pt(b) == 0)
1379 {
1380 // This case only applies for the initial and iterative mesh in
1381 // the adaptation process because the method
1382 // setup_boundary_coordinates() is called by the original mesh
1383 // for boundaries with no GeomObject associated
1384
1385 // We are goind to check if it is necessary to invert the order
1386 // of the zeta values
1387
1388 // Get the first and last node of the current segment and their
1389 // zeta values (arclength)
1390
1391 // There is no need to check for nonhalo elements since the
1392 // container has only nonhalo face elements
1393
1394 // Get access to the first element on the segment
1395 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
1396
1397 // Number of nodes
1398 const unsigned nnod = first_ele_pt->nnode();
1399
1400 // Get the first node of the current segment
1401 Node* first_node_pt = first_ele_pt->node_pt(0);
1402 if (is_inverted[first_ele_pt])
1403 {
1404 first_node_pt = first_ele_pt->node_pt(nnod - 1);
1405 }
1406
1407 // Get access to the last element on the segment
1408 FiniteElement* last_ele_pt = segment_sorted_ele_pt[is].back();
1409
1410 // Get the last node of the current segment
1411 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
1412 if (is_inverted[last_ele_pt])
1413 {
1414 last_node_pt = last_ele_pt->node_pt(0);
1415 }
1416
1417 // Get the zeta coordinates for the first and last node
1418 Vector<double> current_segment_initial_arclen(1);
1419 Vector<double> current_segment_final_arclen(1);
1420 // Is the segment in the current mesh (this) inverted?
1421 if (!Boundary_segment_inverted[b][is]) // Not inverted
1422 {
1423 first_node_pt->get_coordinates_on_boundary(
1424 b, current_segment_initial_arclen);
1425 last_node_pt->get_coordinates_on_boundary(
1426 b, current_segment_final_arclen);
1427 }
1428 else // Inverted
1429 {
1430 first_node_pt->get_coordinates_on_boundary(
1431 b, current_segment_final_arclen);
1432 last_node_pt->get_coordinates_on_boundary(
1433 b, current_segment_initial_arclen);
1434 }
1435
1436 // Once the zeta values have been obtained check if they are set
1437 // in increasing or decreasing order
1438
1439 // Flag to state that the values in the segment are in increasing
1440 // order
1441 bool increasing_order = false;
1442
1443 // If the initial zeta value is smaller than the final zeta
1444 // value then they are in increasing order
1445 if (current_segment_initial_arclen[0] <
1446 current_segment_final_arclen[0])
1447 {
1448 increasing_order = true;
1449 }
1450 // If the initial zeta value is greater than the initial zeta
1451 // value then they are in decreasing order
1452 else if (current_segment_initial_arclen[0] >
1453 current_segment_final_arclen[0])
1454 {
1455 increasing_order = false;
1456 }
1457#ifdef PARANOID
1458 else
1459 {
1460 std::stringstream error_message;
1461 error_message
1462 << "It was not possible to identify if the zeta values on "
1463 << "boundary (" << b << ")\nand segment (" << is
1464 << ") should go in "
1465 << "increasing or decreasing order.\n--- New mesh ---\n"
1466 << "Current segment initial arclength: ("
1467 << current_segment_initial_arclen[0] << ")\n"
1468 << "First node coordinates: (" << first_node_pt->x(0) << ", "
1469 << first_node_pt->x(1) << ")\n"
1470 << "Current segment final arclength: ("
1471 << current_segment_final_arclen[0] << ")\n"
1472 << "Last node coordinates: (" << last_node_pt->x(0) << ", "
1473 << last_node_pt->x(1) << ")\n"
1474 << "Current segment arclength: (" << segment_arclength[is]
1475 << ")\n";
1476 throw OomphLibError(error_message.str(),
1477 OOMPH_CURRENT_FUNCTION,
1478 OOMPH_EXCEPTION_LOCATION);
1479 }
1480#endif
1481
1482 // Now get the original initial and final arclengths and check
1483 // if they are in increasing or decreasing order
1484 const unsigned prev_s = current_segment_to_original_segment_index[is];
1485 const double original_segment_initial_arclength =
1486 original_mesh_pt->boundary_segment_initial_arclength(b)[prev_s];
1487 const double original_segment_final_arclength =
1488 original_mesh_pt->boundary_segment_final_arclength(b)[prev_s];
1489
1490 // Flag to check if the values go in increasing or decreasing
1491 // order in the original mesh segment
1492 bool original_increasing_order = false;
1493
1494 // Now check if the arclengths on the original mesh go in
1495 // increase or decrease order, this is also used to choose the
1496 // starting value to map the values in the current segment
1497 double starting_arclength = 0.0;
1498 if (original_segment_final_arclength >
1499 original_segment_initial_arclength)
1500 {
1501 // ... in increasing order in the original mesh ...
1502 original_increasing_order = true;
1503 // Select the starting arclength
1504 starting_arclength = original_segment_initial_arclength;
1505 }
1506 else if (original_segment_final_arclength <
1507 original_segment_initial_arclength)
1508 {
1509 // ... in decreasing order in the original mesh ...
1510 original_increasing_order = false;
1511 // Select the starting arclength
1512 starting_arclength = original_segment_final_arclength;
1513 }
1514#ifdef PARANOID
1515 else
1516 {
1517 std::stringstream error_message;
1518 error_message
1519 << "It was not possible to identify if the zeta values on "
1520 << "boundary (" << b << ")\nand segment (" << is
1521 << ") should go in "
1522 << "increasing or decreasing order.\n--- Original mesh ---\n"
1523 << "Original segment initial arclength: ("
1524 << original_segment_initial_arclength << ")\n"
1525 << "Original segment final arclength: ("
1526 << original_segment_final_arclength << ")\n";
1527 throw OomphLibError(error_message.str(),
1528 OOMPH_CURRENT_FUNCTION,
1529 OOMPH_EXCEPTION_LOCATION);
1530 }
1531#endif
1532
1533 // Now scale the zeta values based considering if the zeta
1534 // values from the current mesh (this) go in the same order as
1535 // in the original mesh
1536 if (increasing_order && original_increasing_order)
1537 {
1538 // Current seg
1539 // |------|
1540 // 0 ---- 1
1541 //
1542 // Is mapped to the new values
1543 // |------|
1544 // a ---- b
1545 // a = original_segment_initial_arclength
1546 // b = original_segment_final_arclength
1547 // s = starting_arclength
1548 // The mapping is given by
1549 // new_z = s + z_old * (b - a)
1550
1551 // Get the nodes associated to the segment
1552 std::set<Node*> seg_nodes_pt = segment_all_nodes_pt[is];
1553 // Go through all the nodes in the segment an change their
1554 // zeta values
1555 for (std::set<Node*>::iterator it = seg_nodes_pt.begin();
1556 it != seg_nodes_pt.end();
1557 it++)
1558 {
1559 // Storing for the zeta value
1560 Vector<double> zeta(1);
1561 // Get each node
1562 Node* nod_pt = (*it);
1563 // Get the zeta value of the current node
1564 nod_pt->get_coordinates_on_boundary(b, zeta);
1565 // ... and re-assign it
1566 const double temp =
1567 starting_arclength + (zeta[0] * segment_arclength[is]);
1568 // The zeta value
1569 zeta[0] = temp / boundary_arclength;
1570 // Correct
1571 if (std::fabs(zeta[0] - 1.0) < 1.0e-14)
1572 {
1573 zeta[0] = 1.0;
1574 }
1575 else if (std::fabs(zeta[0]) < 1.0e-14)
1576 {
1577 zeta[0] = 0.0;
1578 }
1579
1580 // Set the new value
1581 nod_pt->set_coordinates_on_boundary(b, zeta);
1582 } // Go through all the nodes
1583 } // if (increasing_order && original_increasing_order)
1584 else if (!increasing_order && original_increasing_order)
1585 {
1586 // Current seg
1587 // |------|
1588 // 1 ---- 0
1589 //
1590 // Is mapped to the new values
1591 // |------|
1592 // a ---- b
1593 // a = original_segment_initial_arclength
1594 // b = original_segment_final_arclength
1595 // s = starting_arclength
1596 // The mapping is given by
1597 // new_z = s + (1.0 - z_old) * (b - a)
1598
1599 // Get the nodes associated to the segment
1600 std::set<Node*> seg_nodes_pt = segment_all_nodes_pt[is];
1601 // Go through all the nodes in the segment an change their
1602 // zeta values
1603 for (std::set<Node*>::iterator it = seg_nodes_pt.begin();
1604 it != seg_nodes_pt.end();
1605 it++)
1606 {
1607 // Storing for the zeta value
1608 Vector<double> zeta(1);
1609 // Get each node
1610 Node* nod_pt = (*it);
1611 // Get the zeta value of the current node
1612 nod_pt->get_coordinates_on_boundary(b, zeta);
1613 // ... and re-assign it
1614 const double temp =
1615 starting_arclength + ((1.0 - zeta[0]) * segment_arclength[is]);
1616 // The zeta value
1617 zeta[0] = temp / boundary_arclength;
1618 // Correct
1619 if (std::fabs(zeta[0] - 1.0) < 1.0e-14)
1620 {
1621 zeta[0] = 1.0;
1622 }
1623 else if (std::fabs(zeta[0]) < 1.0e-14)
1624 {
1625 zeta[0] = 0.0;
1626 }
1627 // Set the new value
1628 nod_pt->set_coordinates_on_boundary(b, zeta);
1629 } // Go through all the nodes
1630 } // else if (!increasing_order && original_increasing_order)
1631 else if (increasing_order && !original_increasing_order)
1632 {
1633 // Current seg
1634 // |------|
1635 // 0 ---- 1
1636 //
1637 // Is mapped to the new values
1638 // |------|
1639 // b ---- a
1640 // a = original_segment_initial_arclength
1641 // b = original_segment_final_arclength
1642 // s = starting_arclength
1643 // The mapping is given by
1644 // new_z = s + (1.0 - z_old) * |(b - a)|
1645
1646 // Get the nodes associated to the segment
1647 std::set<Node*> seg_nodes_pt = segment_all_nodes_pt[is];
1648 // Go through all the nodes in the segment an change their
1649 // zeta values
1650 for (std::set<Node*>::iterator it = seg_nodes_pt.begin();
1651 it != seg_nodes_pt.end();
1652 it++)
1653 {
1654 // Storing for the zeta value
1655 Vector<double> zeta(1);
1656 // Get each node
1657 Node* nod_pt = (*it);
1658 // Get the zeta value of the current node
1659 nod_pt->get_coordinates_on_boundary(b, zeta);
1660 // ... and re-assign it
1661 const double temp =
1662 starting_arclength + ((1.0 - zeta[0]) * segment_arclength[is]);
1663 // The zeta value
1664 zeta[0] = temp / boundary_arclength;
1665 // Correct
1666 if (std::fabs(zeta[0] - 1.0) < 1.0e-14)
1667 {
1668 zeta[0] = 1.0;
1669 }
1670 else if (std::fabs(zeta[0]) < 1.0e-14)
1671 {
1672 zeta[0] = 0.0;
1673 }
1674 // Set the new value
1675 nod_pt->set_coordinates_on_boundary(b, zeta);
1676 } // Go through all the nodes
1677 } // else if (increasing_order && !original_increasing_order)
1678 else if (!increasing_order && !original_increasing_order)
1679 {
1680 // Current seg
1681 // |------|
1682 // 0 ---- 1
1683 //
1684 // Is mapped to the new values
1685 // |------|
1686 // a ---- b
1687 // a = original_segment_initial_arclength
1688 // b = original_segment_final_arclength
1689 // s = starting_arclength
1690 // The mapping is given by
1691 // new_z = s + z_old * |(b - a)|
1692
1693 // Get the nodes associated to the segment
1694 std::set<Node*> seg_nodes_pt = segment_all_nodes_pt[is];
1695 // Go through all the nodes in the segment an change their
1696 // zeta values
1697 for (std::set<Node*>::iterator it = seg_nodes_pt.begin();
1698 it != seg_nodes_pt.end();
1699 it++)
1700 {
1701 // Storing for the zeta value
1702 Vector<double> zeta(1);
1703 // Get each node
1704 Node* nod_pt = (*it);
1705 // Get the zeta value of the current node
1706 nod_pt->get_coordinates_on_boundary(b, zeta);
1707 // ... and re-assign it
1708 const double temp =
1709 starting_arclength + (zeta[0] * segment_arclength[is]);
1710 // The zeta value
1711 zeta[0] = temp / boundary_arclength;
1712 // Correct
1713 if (std::fabs(zeta[0] - 1.0) < 1.0e-14)
1714 {
1715 zeta[0] = 1.0;
1716 }
1717 else if (std::fabs(zeta[0]) < 1.0e-14)
1718 {
1719 zeta[0] = 0.0;
1720 }
1721 // Set the new value
1722 nod_pt->set_coordinates_on_boundary(b, zeta);
1723 } // Go through all the nodes
1724 } // else if (!increasing_order && !original_increasing_order)
1725
1726#ifdef PARANOID
1727 // Verify that the z values of the first and last node are not
1728 // out of the range [0,1]
1729 for (std::list<FiniteElement*>::iterator it_list =
1730 segment_sorted_ele_pt[is].begin();
1731 it_list != segment_sorted_ele_pt[is].end();
1732 it_list++)
1733 {
1734 // Number of nodes in the segment
1735 const unsigned nnod = (*it_list)->nnode();
1736
1737 // Get the first node of the current segment
1738 Node* first_node_pt = (*it_list)->node_pt(0);
1739 if (is_inverted[(*it_list)])
1740 {
1741 first_node_pt = (*it_list)->node_pt(nnod - 1);
1742 }
1743
1744 // Get the last node of the current segment
1745 Node* last_node_pt = (*it_list)->node_pt(nnod - 1);
1746 if (is_inverted[(*it_list)])
1747 {
1748 last_node_pt = (*it_list)->node_pt(0);
1749 }
1750
1751 // The z value for the first node
1752 Vector<double> zeta(1);
1753 first_node_pt->get_coordinates_on_boundary(b, zeta);
1754 if (zeta[0] < 0.0 || zeta[0] > 1.0)
1755 {
1756 std::ostringstream error_message;
1757 error_message
1758 << "The boundary coordinate of the first node on boundary ("
1759 << b << ")\nand segment (" << is << ") is out of the "
1760 << "allowed values [0,1]\n"
1761 << "The node boundary coordinate: (" << zeta[0] << ")\n"
1762 << "The vertex coordinates are: (" << first_node_pt->x(0)
1763 << ", " << first_node_pt->x(1) << ")\n";
1764 throw OomphLibError(error_message.str(),
1765 OOMPH_CURRENT_FUNCTION,
1766 OOMPH_EXCEPTION_LOCATION);
1767 }
1768
1769 // The z value for the last node
1770 last_node_pt->get_coordinates_on_boundary(b, zeta);
1771 if (zeta[0] < 0.0 || zeta[0] > 1.0)
1772 {
1773 std::ostringstream error_message;
1774 error_message
1775 << "The boundary coordinate of the last node on boundary (" << b
1776 << ")\nand segment (" << is << ") is out of the "
1777 << "allowed values [0,1]\n"
1778 << "The node boundary coordinate: (" << zeta[0] << ")\n"
1779 << "The vertex coordinates are: (" << last_node_pt->x(0) << ", "
1780 << last_node_pt->x(1) << ")\n";
1781 throw OomphLibError(error_message.str(),
1782 OOMPH_CURRENT_FUNCTION,
1783 OOMPH_EXCEPTION_LOCATION);
1784 }
1785 }
1786#endif // #ifdef PARANOID
1787
1788 } // if (boundary_geom_object_pt(b)==0)
1789
1790 } // for (is < nsegments)
1791
1792 } // if (this != original_mesh_pt)
1793
1794 // ------------------------------------------------------------------
1795 // Copy the corrected (possible reversed) info. to the containers of
1796 // the current mesh
1797 // ------------------------------------------------------------------
1798 // Check if there are segments of b boundary in this processor
1799 if (nsegments > 0)
1800 {
1801 // Copy the initial and final coordinates
1802 Boundary_initial_coordinate[b] =
1803 original_mesh_pt->boundary_initial_coordinate(b);
1804
1805 Boundary_final_coordinate[b] =
1806 original_mesh_pt->boundary_final_coordinate(b);
1807
1808 // The initial and final zeta coordinates (In case of a geometric
1809 // object those are the limits of the geom object)
1810 Boundary_initial_zeta_coordinate[b] =
1811 original_mesh_pt->boundary_initial_zeta_coordinate(b);
1812
1813 Boundary_final_zeta_coordinate[b] =
1814 original_mesh_pt->boundary_final_zeta_coordinate(b);
1815
1816 } // if (nsegments > 0)
1817
1818 // Set the flag to indicate that the zeta values have been assigned
1819 // for the current boundary
1820 Assigned_segments_initial_zeta_values[b] = true;
1821
1822 // Clean all the created face elements
1823 for (unsigned i = 0; i < nele; i++)
1824 {
1825 delete face_el_pt[i];
1826 face_el_pt[i] = 0;
1827 }
1828 }
1829
1830 //======================================================================
1831 /// Compute the boundary segments connectivity for those
1832 /// boundaries that were splited during the distribution process
1833 /// and also the initial zeta values for each segment (the initial
1834 /// and final boundary nodes coordinates)
1835 //======================================================================
1836 template<class ELEMENT>
1839 const unsigned& b)
1840 {
1841 // ------------------------------------------------------------------
1842 // First: Get the face elements associated with the current boundary
1843 // ------------------------------------------------------------------
1844
1845 // Get the communicator of the mesh
1846 OomphCommunicator* comm_pt = this->communicator_pt();
1847
1848 // Get the number of processors
1849 const unsigned nproc = comm_pt->nproc();
1850 // Get the rank of the current processor
1851 const unsigned my_rank = comm_pt->my_rank();
1852
1853 // Temporary storage for face elements
1854 Vector<FiniteElement*> all_face_ele_pt;
1855
1856 // Flag to know whether we are working with an internal open curve
1857 // and then re-assign the initial and final zeta coordinates for
1858 // each segment (only used when the mesh is distributed)
1859 bool is_internal_boundary = false;
1860
1861 // map to associate the face element to the bulk element, necessary
1862 // to attach halo face elements at both sides of each found segment
1863 std::map<FiniteElement*, FiniteElement*> face_to_bulk_element_pt;
1864
1865 // Select the boundary face elements, using the criteria of highest
1866 // processor in charge and bottom-left element
1867 select_boundary_face_elements(
1868 all_face_ele_pt, b, is_internal_boundary, face_to_bulk_element_pt);
1869
1870 // Get the number of face elements
1871 const unsigned n_all_face_ele = all_face_ele_pt.size();
1872
1873 // ----------------------------------------------------------------
1874 // Second: Sort the face elements, only consider nonhalo elements
1875 // ----------------------------------------------------------------
1876
1877 // A flag vector to mark those face elements that are considered as
1878 // halo in the current processor
1879 std::vector<bool> is_halo_face_element(n_all_face_ele, false);
1880
1881 // Count the total number of non halo face elements
1882 unsigned nnon_halo_face_elements = 0;
1883
1884 // Only mark the face elements as halo if the mesh is marked as
1885 // distributed
1886 for (unsigned ie = 0; ie < n_all_face_ele; ie++)
1887 {
1888 FiniteElement* face_ele_pt = all_face_ele_pt[ie];
1889 // Get the bulk element
1890 FiniteElement* tmp_bulk_ele_pt = face_to_bulk_element_pt[face_ele_pt];
1891 // Check if the bulk element is halo
1892 if (!tmp_bulk_ele_pt->is_halo())
1893 {
1894 // Set the flag for non halo element
1895 is_halo_face_element[ie] = false;
1896 // Increase the non halo elements counter
1897 nnon_halo_face_elements++;
1898 }
1899 else
1900 {
1901 // Mark the face element as halo
1902 is_halo_face_element[ie] = true;
1903 }
1904
1905 } // for (ie < n_ele)
1906
1907 // Get the total number of halo face elements
1908 const unsigned nhalo_face_element =
1909 n_all_face_ele - nnon_halo_face_elements;
1910
1911 // The vector of list to store the "segments" that compound the
1912 // boundary (segments may appear only in a distributed mesh)
1913 Vector<std::list<FiniteElement*>> segment_sorted_ele_pt;
1914
1915 // Number of already sorted face elements (only nonhalo elements for
1916 // a distributed mesh)
1917 unsigned nsorted_face_elements = 0;
1918
1919 // Keep track of who's done (this apply to nonhalo only, remember we
1920 // are only working with halo elements)
1921 std::map<FiniteElement*, bool> done_el;
1922
1923 // Keep track of which element is inverted (in distributed mesh the
1924 // elements may be inverted with respect to the segment they belong)
1925 std::map<FiniteElement*, bool> is_inverted;
1926
1927 // Iterate until all possible segments have been created
1928 while (nsorted_face_elements < nnon_halo_face_elements)
1929 {
1930 // The ordered list of face elements (in a distributed mesh a
1931 // collection of contiguous face elements define a segment)
1932 std::list<FiniteElement*> sorted_el_pt;
1933 sorted_el_pt.clear();
1934
1935#ifdef PARANOID
1936 // Select an initial element for the segment (the first not done
1937 // nonhalo element)
1938 bool found_initial_face_element = false;
1939#endif
1940
1941 FiniteElement* ele_face_pt = 0;
1942
1943 unsigned iface = 0;
1944 for (iface = 0; iface < n_all_face_ele; iface++)
1945 {
1946 if (!is_halo_face_element[iface])
1947 {
1948 ele_face_pt = all_face_ele_pt[iface];
1949 // If not done then take it as initial face element
1950 if (!done_el[ele_face_pt])
1951 {
1952#ifdef PARANOID
1953 found_initial_face_element = true;
1954#endif
1955 nsorted_face_elements++;
1956 iface++; // The next element number
1957 sorted_el_pt.push_back(ele_face_pt);
1958 // Mark as done
1959 done_el[ele_face_pt] = true;
1960 break;
1961 }
1962 }
1963 } // for (iface < nele)
1964
1965#ifdef PARANOID
1966 if (!found_initial_face_element)
1967 {
1968 std::ostringstream error_message;
1969 error_message
1970 << "Could not find an initial face element for the current segment\n";
1971 // << "----- Possible memory leak -----\n";
1972 throw OomphLibError(error_message.str(),
1973 OOMPH_CURRENT_FUNCTION,
1974 OOMPH_EXCEPTION_LOCATION);
1975 }
1976#endif
1977
1978 // Number of nodes
1979 const unsigned nnod = ele_face_pt->nnode();
1980
1981 // Left and rightmost nodes (the left and right nodes of the
1982 // current face element)
1983 Node* left_node_pt = ele_face_pt->node_pt(0);
1984 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
1985
1986 // Continue iterating if a new face element has been added to the
1987 // list
1988 bool face_element_added = false;
1989
1990 // While a new face element has been added to the set of sorted
1991 // face elements then re-iterate
1992 do
1993 {
1994 // Start from the next face element since we have already added
1995 // the previous one as the initial face element (any previous
1996 // face element had to be added on previous iterations)
1997 for (unsigned iiface = iface; iiface < n_all_face_ele; iiface++)
1998 {
1999 // Re-start flag
2000 face_element_added = false;
2001
2002 // Get the candidate element
2003 ele_face_pt = all_face_ele_pt[iiface];
2004
2005 // Check that the candidate element has not been done and is
2006 // not a halo element
2007 if (!(done_el[ele_face_pt] || is_halo_face_element[iiface]))
2008 {
2009 // Get the left and right nodes of the current element
2010 Node* local_left_node_pt = ele_face_pt->node_pt(0);
2011 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
2012
2013 // New element fits at the left of segment and is not inverted
2014 if (left_node_pt == local_right_node_pt)
2015 {
2016 left_node_pt = local_left_node_pt;
2017 sorted_el_pt.push_front(ele_face_pt);
2018 is_inverted[ele_face_pt] = false;
2019 face_element_added = true;
2020 }
2021 // New element fits at the left of segment and is inverted
2022 else if (left_node_pt == local_left_node_pt)
2023 {
2024 left_node_pt = local_right_node_pt;
2025 sorted_el_pt.push_front(ele_face_pt);
2026 is_inverted[ele_face_pt] = true;
2027 face_element_added = true;
2028 }
2029 // New element fits on the right of segment and is not inverted
2030 else if (right_node_pt == local_left_node_pt)
2031 {
2032 right_node_pt = local_right_node_pt;
2033 sorted_el_pt.push_back(ele_face_pt);
2034 is_inverted[ele_face_pt] = false;
2035 face_element_added = true;
2036 }
2037 // New element fits on the right of segment and is inverted
2038 else if (right_node_pt == local_right_node_pt)
2039 {
2040 right_node_pt = local_left_node_pt;
2041 sorted_el_pt.push_back(ele_face_pt);
2042 is_inverted[ele_face_pt] = true;
2043 face_element_added = true;
2044 }
2045
2046 if (face_element_added)
2047 {
2048 done_el[ele_face_pt] = true;
2049 nsorted_face_elements++;
2050 break;
2051 }
2052
2053 } // if (!(done_el[ele_face_pt] || is_halo_face_element[iiface]))
2054 } // for (iiface<nnon_halo_face_element)
2055 } while (face_element_added &&
2056 (nsorted_face_elements < nnon_halo_face_elements));
2057
2058 // Store the created segment in the vector of segments
2059 segment_sorted_ele_pt.push_back(sorted_el_pt);
2060
2061 } // while(nsorted_face_elements < nnon_halo_face_elements);
2062
2063 // -----------------------------------------------------------------
2064 // Third: We have the face elements sorted (in segments), now assign
2065 // boundary coordinates to the nodes in the segments, this is the
2066 // LOCAL boundary coordinate and further communication is needed to
2067 // compute the GLOBAL boundary coordinates
2068 // -----------------------------------------------------------------
2069
2070 // Vector of sets that stores the nodes of each segment based on a
2071 // lexicographically order starting from the bottom left node of
2072 // each segment
2073 Vector<std::set<Node*>> segment_all_nodes_pt;
2074
2075 // The number of segments in this processor
2076 const unsigned nsegments = segment_sorted_ele_pt.size();
2077 // DEBP(nsegments);
2078
2079#ifdef PARANOID
2080 if (nnon_halo_face_elements > 0 && nsegments == 0)
2081 {
2082 std::ostringstream error_message;
2083 error_message
2084 << "The number of segments is zero, but the number of nonhalo\n"
2085 << "elements is: (" << nnon_halo_face_elements << ")\n";
2086 throw OomphLibError(
2087 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
2088 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
2089#endif
2090
2091 // The arclength of each segment in the current processor
2092 Vector<double> segment_arclength(nsegments);
2093
2094 // The number of vertices of each segment
2095 Vector<unsigned> nvertices_per_segment(nsegments);
2096
2097 // The initial zeta for the segment
2098 Vector<double> initial_zeta_segment(nsegments);
2099
2100 // The final zeta for the segment
2101 Vector<double> final_zeta_segment(nsegments);
2102
2103 // Go through all the segments and compute its ARCLENGTH (if the
2104 // boundary has a GeomObject associated then assign the initial and
2105 // final zeta values for the segment)
2106 for (unsigned is = 0; is < nsegments; is++)
2107 {
2108#ifdef PARANOID
2109 if (segment_sorted_ele_pt[is].size() == 0)
2110 {
2111 std::ostringstream error_message;
2112 error_message << "The (" << is << ")-th segment has no elements\n";
2113 throw OomphLibError(error_message.str(),
2114 OOMPH_CURRENT_FUNCTION,
2115 OOMPH_EXCEPTION_LOCATION);
2116 } // if (segment_sorted_ele_pt[is].size() == 0)
2117#endif
2118
2119 // Get access to the first element on the segment
2120 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
2121
2122 // Number of nodes
2123 const unsigned nnod = first_ele_pt->nnode();
2124
2125 // Get the first node of the current segment
2126 Node* first_node_pt = first_ele_pt->node_pt(0);
2127 if (is_inverted[first_ele_pt])
2128 {
2129 first_node_pt = first_ele_pt->node_pt(nnod - 1);
2130 }
2131
2132 // Coordinates of left node
2133 double x_left = first_node_pt->x(0);
2134 double y_left = first_node_pt->x(1);
2135
2136 // Initialise boundary coordinate (local boundary coordinate for
2137 // boundaries with more than one segment)
2138 Vector<double> zeta(1, 0.0);
2139
2140 // If we have associated a GeomObject then it is not necessary to
2141 // compute the arclength, only read the values from the nodes at
2142 // the edges and set the initial and final zeta segment values
2143 if (this->boundary_geom_object_pt(b) != 0)
2144 {
2145 // Get the initial node coordinate
2146 first_node_pt->get_coordinates_on_boundary(b, zeta);
2147 // Set the initial zeta segment value
2148 initial_zeta_segment[is] = zeta[0];
2149
2150 // Get access to the last element on the segment
2151 FiniteElement* last_ele_pt = segment_sorted_ele_pt[is].back();
2152
2153 // Get the last node of the current segment
2154 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
2155 if (is_inverted[last_ele_pt])
2156 {
2157 last_node_pt = last_ele_pt->node_pt(0);
2158 }
2159
2160 // Get the final node coordinate
2161 last_node_pt->get_coordinates_on_boundary(b, zeta);
2162 // Set the final zeta segment value
2163 final_zeta_segment[is] = zeta[0];
2164 }
2165
2166 // Sort the nodes in the segment (lexicographically bottom left
2167 // node)
2168 std::set<Node*> local_nodes_pt;
2169 // Insert the first node
2170 local_nodes_pt.insert(first_node_pt);
2171
2172 // Now loop over nodes in order and increase the ARCLENGTH
2173 for (std::list<FiniteElement*>::iterator it =
2174 segment_sorted_ele_pt[is].begin();
2175 it != segment_sorted_ele_pt[is].end();
2176 it++)
2177 {
2178 // Get the pointer to the element
2179 FiniteElement* el_pt = (*it);
2180
2181 // Start node and increment
2182 unsigned k_nod = 1;
2183 int nod_diff = 1;
2184 // Access nodes in reverse?
2185 if (is_inverted[el_pt])
2186 {
2187 k_nod = nnod - 2;
2188 nod_diff = -1;
2189 }
2190
2191 // Loop over nodes in the face element
2192 for (unsigned j = 1; j < nnod; j++)
2193 {
2194 Node* nod_pt = el_pt->node_pt(k_nod);
2195 k_nod += nod_diff;
2196
2197 // Coordinates of right node
2198 double x_right = nod_pt->x(0);
2199 double y_right = nod_pt->x(1);
2200
2201 // Increment boundary coordinate (the arclength)
2202 zeta[0] += sqrt((x_right - x_left) * (x_right - x_left) +
2203 (y_right - y_left) * (y_right - y_left));
2204
2205 // When we have a GeomObject associated to the boundary we already
2206 // know the zeta values for the nodes, there is no need to compute
2207 // the arclength
2208 // if (this->boundary_geom_object_pt(b)==0)
2209 // {
2210 // // Set boundary coordinate
2211 // // nod_pt->set_coordinates_on_boundary(b, zeta);
2212 // }
2213
2214 // Increment reference coordinate
2215 x_left = x_right;
2216 y_left = y_right;
2217
2218 // Get lexicographically bottom left node but only
2219 // use vertex nodes as candidates
2220 local_nodes_pt.insert(nod_pt);
2221
2222 } // for (j < nnod)
2223
2224 } // iterator over the elements in the segment
2225
2226 // Info. to be passed to other processors
2227 // The initial arclength for the segment that goes after this depends
2228 // on the current segment arclength
2229 segment_arclength[is] = zeta[0];
2230
2231 // Info. to be passed to the other processors
2232 // The initial vertex number for the segment that goes after this
2233 // depends on the current segment vertices number
2234 nvertices_per_segment[is] = local_nodes_pt.size();
2235
2236 // Add the nodes for the corresponding segment in the container
2237 segment_all_nodes_pt.push_back(local_nodes_pt);
2238
2239 // The attaching of the halo elements at both sides of the segments is
2240 // performed only if segments connectivity needs to be computed
2241
2242 } // for (is < nsegments)
2243
2244 // Container to store the number of vertices before each segment,
2245 // initialise to zero in case we have a non distributed boundary
2246 Vector<unsigned> nvertices_before_segment(nsegments, 0);
2247
2248 // Store the initial arclength for each segment of boundary in the
2249 // current processor, initalise to zero in case we have a non
2250 // distributed boundary
2251 Vector<double> initial_segment_arclength(nsegments, 0.0);
2252
2253 // Info. to be passed to other processors
2254 // If the boundary is distributed we need to know which processors does
2255 // have the initial and final segments, this helps to get the first and
2256 // last nodes coordinates (info. used to scale the bound coordinates)
2257
2258 // Processors with the initial and final segment
2259 unsigned proc_with_initial_seg = 0;
2260 unsigned proc_with_final_seg = 0;
2261
2262 // ... and the index of those segments (only of interest in the
2263 // processors that have the initial and final segments)
2264 unsigned initial_segment = 0;
2265 unsigned final_segment = 0;
2266
2267 // Each segment needs to know whether it has to be inverted or not
2268 // Store whether a segment needs to be inverted or not
2269 Vector<unsigned> segment_inverted(nsegments);
2270
2271 // Before attaching the halo elements create a copy of the data
2272 // structure without halo elements
2273 Vector<std::list<FiniteElement*>> segment_sorted_nonhalo_ele_pt(nsegments);
2274 for (unsigned is = 0; is < nsegments; is++)
2275 {
2276 for (std::list<FiniteElement*>::iterator it_seg =
2277 segment_sorted_ele_pt[is].begin();
2278 it_seg != segment_sorted_ele_pt[is].end();
2279 it_seg++)
2280 {
2281 segment_sorted_nonhalo_ele_pt[is].push_back((*it_seg));
2282 }
2283
2284 } // for (is < nsegments)
2285
2286 // --------------------------------------------------------------
2287 // Attach the halo elements at both sides of the segments
2288 for (unsigned is = 0; is < nsegments; is++)
2289 {
2290 // Get access to the first element on the segment
2291 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
2292
2293 // Number of nodes
2294 const unsigned nnod = first_ele_pt->nnode();
2295
2296 // Get the first node of the current segment
2297 Node* first_node_pt = first_ele_pt->node_pt(0);
2298 if (is_inverted[first_ele_pt])
2299 {
2300 first_node_pt = first_ele_pt->node_pt(nnod - 1);
2301 }
2302
2303 // Get access to the last element on the segment
2304 FiniteElement* last_ele_pt = segment_sorted_ele_pt[is].back();
2305
2306 // Get the last node of the current segment
2307 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
2308 if (is_inverted[last_ele_pt])
2309 {
2310 last_node_pt = last_ele_pt->node_pt(0);
2311 }
2312
2313 // -----------------------------------------------------------------
2314 // Fourth: Now attach the halo elements to the left and right side
2315 // of each segment
2316 // -----------------------------------------------------------------
2317 bool attached_left_halo = false;
2318 bool attached_right_halo = false;
2319 if (nhalo_face_element > 0)
2320 {
2321 for (unsigned iiface = 0; iiface < n_all_face_ele; iiface++)
2322 {
2323 // Get the candidate element
2324 FiniteElement* halo_face_ele_pt = all_face_ele_pt[iiface];
2325
2326 // Check that the element is a halo face element, we do not check
2327 // if the element has been already done since the halo elements
2328 // may be connected to more than one segment (2 at most), to the
2329 // left and right of different segments
2330 //
2331 // Segment k Halo Segment r
2332 // |---|---|---| |xxx| |---|---|---|
2333 //
2334 // Segment k Halo Segment r
2335 // |---|---|---|xxx|---|---|---|
2336 //
2337 if (is_halo_face_element[iiface])
2338 {
2339 // Get its left and right nodes
2340 Node* left_node_pt = halo_face_ele_pt->node_pt(0);
2341 Node* right_node_pt = halo_face_ele_pt->node_pt(nnod - 1);
2342 // The halo element fits to the left of segment
2343 if (!attached_left_halo && (first_node_pt == right_node_pt ||
2344 first_node_pt == left_node_pt))
2345 {
2346 // Add the halo element to the left of the segment
2347 segment_sorted_ele_pt[is].push_front(halo_face_ele_pt);
2348
2349 // Once a halo face element has been added to the left
2350 // mark as found halo to the left
2351 attached_left_halo = true;
2352 }
2353 // The halo element fits to the right of the segment
2354 else if (!attached_right_halo && (last_node_pt == left_node_pt ||
2355 last_node_pt == right_node_pt))
2356 {
2357 // Add the halo element to the right of the segment
2358 segment_sorted_ele_pt[is].push_back(halo_face_ele_pt);
2359 // Once a halo face element has been added to the right
2360 // mark as found halo to the right
2361 attached_right_halo = true;
2362 }
2363 // If we have already found elements to left and right then
2364 // break the loop
2365 if (attached_left_halo && attached_right_halo)
2366 {
2367 break;
2368 }
2369
2370 } // if (is_halo_face_element[iiface])
2371
2372 } // for (iiface < nel)
2373
2374 } // if (nhalo_face_element > 0)
2375
2376 } // for (is < nsegments)
2377
2378 // The segments now have local coordinates assigned and halo
2379 // elements attached to them. Store that info. in the corresponding
2380 // data structures and be ready to send that info. to a root
2381 // processor. The root processor will be in charge of computing the
2382 // boundary coordinates for each segment of the boundary.
2383
2384 // For each segment store the following information
2385 // --------------------------------------------------------------------
2386 // Stores the "rank" of the processor to the left of each segment,
2387 // zero if there is no processor to the left which states that the
2388 // segment is the first one on the boundary
2389 Vector<unsigned> left_processor_plus_one(nsegments);
2390
2391 // Stores the "rank" of the processor to the right of each segment,
2392 // zero if there is no processor to the right which states that the
2393 // segment is the last one on the boundary
2394 Vector<unsigned> right_processor_plus_one(nsegments);
2395
2396 // The id. of the halo element to the left of the segment, note that
2397 // this info. is not necessary if there is no processor to the left
2398 // of the segment
2399 Vector<unsigned> left_halo_element(nsegments);
2400
2401 // The id. of the halo element to the right of the segment, note that
2402 // this info. is not necessary if there is no processor to the right
2403 // of the segment
2404 Vector<unsigned> right_halo_element(nsegments);
2405
2406 // The id. of the haloed element to the left of the segment, note that
2407 // this info. is not necessary if there is no processor to the left
2408 // of the segment
2409 Vector<unsigned> left_haloed_element(nsegments);
2410
2411 // The id. of the haloed element to the right of the segment, note
2412 // that this info. is not necessary if there is no processor to the
2413 // right of the segment
2414 Vector<unsigned> right_haloed_element(nsegments);
2415
2416 // Go through all the segments and get the info.
2417 for (unsigned is = 0; is < nsegments; is++)
2418 {
2419 // Get access to the left most face element on the segment
2420 FiniteElement* left_face_ele_pt = segment_sorted_ele_pt[is].front();
2421
2422 // Get the corresponding bulk element and check whether it is a halo
2423 // element or not
2424 FiniteElement* tmp_left_bulk_ele_pt =
2425 face_to_bulk_element_pt[left_face_ele_pt];
2426
2427 // Check if the bulk element is halo
2428 if (tmp_left_bulk_ele_pt->is_halo())
2429 {
2430 // Then store the corresponding info.
2431 int left_proc = tmp_left_bulk_ele_pt->non_halo_proc_ID();
2432#ifdef PARANOID
2433 if (left_proc < 0)
2434 {
2435 std::ostringstream error_message;
2436 error_message
2437 << "The current bulk element (left) is marked as halo but "
2438 << "the processor holding\nthe non-halo counterpart is "
2439 << "negative!\n";
2440 throw OomphLibError(error_message.str(),
2441 OOMPH_CURRENT_FUNCTION,
2442 OOMPH_EXCEPTION_LOCATION);
2443 }
2444#endif
2445 // The processor "rank" to the left
2446 unsigned left_processor = static_cast<unsigned>(left_proc);
2447 left_processor_plus_one[is] = left_processor + 1;
2448
2449 // Now get the id of the halo element to the left
2450 GeneralisedElement* left_element_pt = tmp_left_bulk_ele_pt;
2451
2452 // Get the halo elements with left processor
2453 Vector<GeneralisedElement*> left_halo_element_pt =
2454 this->halo_element_pt(left_processor);
2455
2456#ifdef PARANOID
2457 // Flag to state that the halo element was found
2458 bool left_halo_element_found = false;
2459#endif
2460
2461 const unsigned n_halo_left = left_halo_element_pt.size();
2462 for (unsigned lh = 0; lh < n_halo_left; lh++)
2463 {
2464 if (left_element_pt == left_halo_element_pt[lh])
2465 {
2466 left_halo_element[is] = lh;
2467#ifdef PARANOID
2468 left_halo_element_found = true;
2469#endif
2470 break;
2471 }
2472 } // for (lh < n_halo_left)
2473
2474#ifdef PARANOID
2475 if (!left_halo_element_found)
2476 {
2477 std::ostringstream error_message;
2478 error_message
2479 << "The current bulk element (left) marked as halo was "
2480 << "not found in the vector of halo\nelements associated "
2481 << "with the (" << left_processor << ") processor.\n\n";
2482 throw OomphLibError(error_message.str(),
2483 OOMPH_CURRENT_FUNCTION,
2484 OOMPH_EXCEPTION_LOCATION);
2485 } // if (!left_halo_element_found)
2486#endif
2487
2488 // Get the left-most nonhalo element (use the backup list of
2489 // nonhalo elements)
2490 left_face_ele_pt = segment_sorted_nonhalo_ele_pt[is].front();
2491
2492 // Get the corresponding bulk element
2493 tmp_left_bulk_ele_pt = face_to_bulk_element_pt[left_face_ele_pt];
2494
2495#ifdef PARANOID
2496 // This element should not be marked as halo
2497 if (tmp_left_bulk_ele_pt->is_halo())
2498 {
2499 std::ostringstream error_message;
2500 error_message
2501 << "The bulk element represetation of the left-most nonhalo face\n"
2502 << "element of the current segment (" << is
2503 << ") is marked as halo,\n"
2504 << "but the face element created from it is nonhalo\n";
2505 throw OomphLibError(error_message.str(),
2506 OOMPH_CURRENT_FUNCTION,
2507 OOMPH_EXCEPTION_LOCATION);
2508 } // if (tmp_left_bulk_ele_pt->is_halo())
2509#endif
2510
2511 // Cast from "FiniteElement*" to "GeneralisedElement*" to be able
2512 // to search in the haloed vector
2513 left_element_pt = tmp_left_bulk_ele_pt;
2514
2515#ifdef PARANOID
2516 // Flag to state that the haloed element was found
2517 bool left_haloed_element_found = false;
2518#endif
2519
2520 // Now get the id for the haloed element to the left, get the
2521 // haloed elements from the processor to the left
2522 Vector<GeneralisedElement*> left_haloed_element_pt =
2523 this->haloed_element_pt(left_processor);
2524
2525 const unsigned nhaloed_left = left_haloed_element_pt.size();
2526 for (unsigned lhd = 0; lhd < nhaloed_left; lhd++)
2527 {
2528 if (left_element_pt == left_haloed_element_pt[lhd])
2529 {
2530 left_haloed_element[is] = lhd;
2531#ifdef PARANOID
2532 left_haloed_element_found = true;
2533#endif
2534 break;
2535 }
2536 } // for (lhd < nhaloed_left)
2537
2538#ifdef PARANOID
2539 if (!left_haloed_element_found)
2540 {
2541 std::ostringstream error_message;
2542 error_message
2543 << "The current bulk element (left) marked as haloed was "
2544 << "not found in the vector of haloed\nelements associated "
2545 << "with processor (" << left_processor << ").\n";
2546 throw OomphLibError(error_message.str(),
2547 OOMPH_CURRENT_FUNCTION,
2548 OOMPH_EXCEPTION_LOCATION);
2549 }
2550#endif
2551 } // if (tmp_left_bulk_ele_pt->is_halo())
2552 else
2553 {
2554 // If not halo then state the info. to indicate that
2555 left_processor_plus_one[is] = 0;
2556 // Null this info.
2557 left_halo_element[is] = 0;
2558 // Null this info.
2559 left_haloed_element[is] = 0;
2560 }
2561
2562 // Get access to the right most face element on the segment
2563 FiniteElement* right_face_ele_pt = segment_sorted_ele_pt[is].back();
2564
2565 // Get the corresponding bulk element and check whether it is
2566 // a halo element or not
2567 FiniteElement* tmp_right_bulk_ele_pt =
2568 face_to_bulk_element_pt[right_face_ele_pt];
2569
2570 // Check if the bulk element is halo
2571 if (tmp_right_bulk_ele_pt->is_halo())
2572 {
2573 // Then store the corresponding info.
2574 int right_proc = tmp_right_bulk_ele_pt->non_halo_proc_ID();
2575#ifdef PARANOID
2576 if (right_proc < 0)
2577 {
2578 std::ostringstream error_message;
2579 error_message
2580 << "The current bulk element (right) is marked as halo but "
2581 << "the processor holding\nthe non-halo counterpart is "
2582 << "negative!\n";
2583 throw OomphLibError(error_message.str(),
2584 "TriangleMesh::compute_boundary_segments_"
2585 "connectivity_and_initial_zeta_values()",
2586 OOMPH_EXCEPTION_LOCATION);
2587 }
2588#endif
2589 // The processor "rank" to the right
2590 unsigned right_processor = static_cast<unsigned>(right_proc);
2591 right_processor_plus_one[is] = right_processor + 1;
2592
2593 // Now get the id of the halo element to the right
2594 GeneralisedElement* right_element_pt = tmp_right_bulk_ele_pt;
2595
2596 // Get the halo elements with right processor
2597 Vector<GeneralisedElement*> right_halo_element_pt =
2598 this->halo_element_pt(right_processor);
2599
2600#ifdef PARANOID
2601 // Flag to state that the halo element was found
2602 bool right_halo_element_found = false;
2603#endif
2604
2605 const unsigned nhalo_right = right_halo_element_pt.size();
2606 for (unsigned rh = 0; rh < nhalo_right; rh++)
2607 {
2608 if (right_element_pt == right_halo_element_pt[rh])
2609 {
2610 right_halo_element[is] = rh;
2611#ifdef PARANOID
2612 right_halo_element_found = true;
2613#endif
2614 break;
2615 }
2616 } // for (rh < nhalo_right)
2617#ifdef PARANOID
2618 if (!right_halo_element_found)
2619 {
2620 std::ostringstream error_message;
2621 error_message
2622 << "The current bulk element (right) marked as halo was not "
2623 << "found in the vector of halo\nelements associated with "
2624 << "the (" << right_processor << ") processor.\n\n";
2625 throw OomphLibError(error_message.str(),
2626 "TriangleMesh::compute_boundary_segments_"
2627 "connectivity_and_initial_zeta_values()",
2628 OOMPH_EXCEPTION_LOCATION);
2629 }
2630#endif
2631
2632 // Get the right-most nonhalo element (use the backup list of
2633 // nonhalo elements)
2634 right_face_ele_pt = segment_sorted_nonhalo_ele_pt[is].back();
2635
2636 // Get the corresponding bulk element
2637 tmp_right_bulk_ele_pt = face_to_bulk_element_pt[right_face_ele_pt];
2638#ifdef PARANOID
2639 // This element should not be marked as halo
2640 if (tmp_right_bulk_ele_pt->is_halo())
2641 {
2642 std::ostringstream error_message;
2643 error_message
2644 << "The bulk element represetation of the right-most nonhalo face\n"
2645 << "element of the current segment (" << is
2646 << ") is marked as halo,\n"
2647 << "but the face element created from it is nonhalo\n";
2648 throw OomphLibError(error_message.str(),
2649 "TriangleMesh::compute_boundary_segments_"
2650 "connectivity_and_initial_zeta_values()",
2651 OOMPH_EXCEPTION_LOCATION);
2652 } // if (tmp_right_bulk_ele_pt->is_halo())
2653#endif
2654
2655 // Cast from "FiniteElement*" to "GeneralisedElement*" to be able
2656 // to search in the haloed vector
2657 right_element_pt = tmp_right_bulk_ele_pt;
2658
2659#ifdef PARANOID
2660 // Flag to state that the haloed element was found
2661 bool right_haloed_element_found = false;
2662#endif
2663
2664 // Now get the id for the haloed element to the right
2665 Vector<GeneralisedElement*> right_haloed_element_pt =
2666 this->haloed_element_pt(right_processor);
2667
2668 const unsigned nhaloed_right = right_haloed_element_pt.size();
2669 for (unsigned rhd = 0; rhd < nhaloed_right; rhd++)
2670 {
2671 if (right_element_pt == right_haloed_element_pt[rhd])
2672 {
2673 right_haloed_element[is] = rhd;
2674#ifdef PARANOID
2675 right_haloed_element_found = true;
2676#endif
2677 break;
2678 }
2679 } // for (rhd < nhaloed_right)
2680
2681#ifdef PARANOID
2682 if (!right_haloed_element_found)
2683 {
2684 std::ostringstream error_message;
2685 error_message
2686 << "The current bulk element (right) marked as haloed was not "
2687 << "found in the vector of haloed\nelements associated with "
2688 << "the (" << right_processor << ") processor.\n\n";
2689 throw OomphLibError(error_message.str(),
2690 "TriangleMesh::compute_boundary_segments_"
2691 "connectivity_and_initial_zeta_values()",
2692 OOMPH_EXCEPTION_LOCATION);
2693 }
2694#endif
2695
2696 } // if (tmp_right_bulk_ele_pt->is_halo())
2697 else
2698 {
2699 // If not halo then state the info. to indicate that
2700 right_processor_plus_one[is] = 0;
2701 // Null this info.
2702 right_halo_element[is] = 0;
2703 // Null this info.
2704 right_haloed_element[is] = 0;
2705 }
2706
2707 } // for (is < nsegments). Used to get the halo info. of the
2708 // segments
2709
2710 // Now we have all the info. to be sent to the root processor and
2711 // compute the correct (global) boundary coordinates for the current
2712 // boundary
2713
2714 // The root processor will be in charge of performing the computing
2715 // of the coordinate values along the boundary, all the other
2716 // processors only send their info. and wait for receiving the new
2717 // starting values for each of its segments
2718
2719 // Choose the root processor
2720 const unsigned root_processor = 0;
2721 // ------------------------------------------------------------------
2722 // Starts the MPI stage
2723
2724 // The root processor receives the number of segments of each
2725 // processor associated to the current boundary
2726 Vector<unsigned> root_nsegments_per_processor(nproc);
2727 unsigned nsegments_mpi = nsegments;
2728 MPI_Gather(&nsegments_mpi,
2729 1,
2730 MPI_UNSIGNED,
2731 &root_nsegments_per_processor[0],
2732 1,
2733 MPI_UNSIGNED,
2734 root_processor,
2735 comm_pt->mpi_comm());
2736
2737 // Package the info. and prepare it to be sent
2738 // For the packaged info. we send 7 data per each segment, the indexes
2739 // are as follow; 0 left proc, 1 right proc, 2 left halo, 3 right
2740 // halo, 4 left haloed, 5 right haloed and 6 for nvertices per
2741 // segment
2742 // The size of the package (unsigned)
2743 const unsigned spu = 7;
2744 Vector<unsigned> flat_packed_unsigned_send_data(nsegments * spu);
2745 for (unsigned is = 0; is < nsegments; is++)
2746 {
2747 flat_packed_unsigned_send_data[(spu * is) + 0] =
2748 left_processor_plus_one[is];
2749 flat_packed_unsigned_send_data[(spu * is) + 1] =
2750 right_processor_plus_one[is];
2751 flat_packed_unsigned_send_data[(spu * is) + 2] = left_halo_element[is];
2752 flat_packed_unsigned_send_data[(spu * is) + 3] = right_halo_element[is];
2753 flat_packed_unsigned_send_data[(spu * is) + 4] = left_haloed_element[is];
2754 flat_packed_unsigned_send_data[(spu * is) + 5] = right_haloed_element[is];
2755 flat_packed_unsigned_send_data[(spu * is) + 6] =
2756 nvertices_per_segment[is];
2757 }
2758
2759 // How many data will this processor send
2760 const unsigned nudata_to_send = flat_packed_unsigned_send_data.size();
2761
2762 // How many data does the root processor will receive from each
2763 // processor
2764 Vector<int> root_nudata_to_receive(nproc, 0);
2765 // Total number of data to receive from all processors
2766 unsigned root_nutotal_data_receive = 0;
2767 for (unsigned ip = 0; ip < nproc; ip++)
2768 {
2769 // Compute the number of data the root processor will receive from
2770 // each processor
2771 root_nudata_to_receive[ip] = root_nsegments_per_processor[ip] * spu;
2772 // Add on the total number of data to receive
2773 root_nutotal_data_receive += root_nudata_to_receive[ip];
2774 }
2775
2776 // Stores and compute the offsets (in root) for the data received
2777 // from each processor
2778 Vector<int> root_uoffsets_receive(nproc, 0);
2779 root_uoffsets_receive[0] = 0;
2780 for (unsigned ip = 1; ip < nproc; ip++)
2781 {
2782 // Compute the offset to store the values from each processor
2783 root_uoffsets_receive[ip] =
2784 root_uoffsets_receive[ip - 1] + root_nudata_to_receive[ip - 1];
2785 }
2786
2787 // Create at least one entry so we don't get a seg fault below
2788 if (flat_packed_unsigned_send_data.size() == 0)
2789 {
2790 flat_packed_unsigned_send_data.resize(1);
2791 }
2792
2793 // Vector where to receive the info.
2794 Vector<unsigned> flat_packed_unsigned_receive_data(
2795 root_nutotal_data_receive);
2796 if (my_rank != root_processor)
2797 {
2798 // Create at least one entry so we don't get a seg fault below
2799 if (flat_packed_unsigned_receive_data.size() == 0)
2800 {
2801 flat_packed_unsigned_receive_data.resize(1);
2802 }
2803 } // if (my_rank!=root_processor)
2804
2805 MPI_Gatherv(&flat_packed_unsigned_send_data[0], // Flat package to
2806 // send info. from
2807 // each processor
2808 nudata_to_send, // Total number of data to send from
2809 // each processor
2810 MPI_UNSIGNED,
2811 &flat_packed_unsigned_receive_data[0], // Container
2812 // where to
2813 // receive the
2814 // info. from all
2815 // the processors
2816 &root_nudata_to_receive[0], // Number of data to receive
2817 // from each processor
2818 &root_uoffsets_receive[0], // The offset to store the
2819 // info. from each processor
2820 MPI_UNSIGNED,
2821 root_processor, // The processor that receives all the
2822 // info.
2823 comm_pt->mpi_comm());
2824
2825 // Clear the flat package to send
2826 flat_packed_unsigned_send_data.clear();
2827 flat_packed_unsigned_send_data.resize(0);
2828
2829 // Package the info. and prepare it to be sent
2830 // For the packaged info. we send 1 data per each segment which is
2831 // at the moment the arclength of each segment
2832 // The size of the package
2833 const unsigned spd = 1;
2834 Vector<double> flat_packed_double_send_data(nsegments * spd);
2835 for (unsigned is = 0; is < nsegments; is++)
2836 {
2837 flat_packed_double_send_data[(spd * is) + 0] = segment_arclength[is];
2838 }
2839
2840 // How many data will this processor send
2841 const unsigned nddata_to_send = flat_packed_double_send_data.size();
2842 // How many data does the root processor will receive from each
2843 // processor
2844 Vector<int> root_nddata_to_receive(nproc, 0);
2845 // Total number of data to receive from all processors
2846 unsigned root_ndtotal_data_receive = 0;
2847 for (unsigned ip = 0; ip < nproc; ip++)
2848 {
2849 root_nddata_to_receive[ip] = root_nsegments_per_processor[ip] * spd;
2850 root_ndtotal_data_receive += root_nddata_to_receive[ip];
2851 }
2852
2853 // Stores and compute the offsets for the data received from each
2854 // processor
2855 Vector<int> root_doffsets_receive(nproc, 0);
2856 root_doffsets_receive[0] = 0;
2857 for (unsigned ip = 1; ip < nproc; ip++)
2858 {
2859 // Compute the offset to store the values from each processor
2860 root_doffsets_receive[ip] =
2861 root_doffsets_receive[ip - 1] + root_nddata_to_receive[ip - 1];
2862 }
2863
2864 // Create at least one entry so we don't get a seg fault below
2865 if (flat_packed_double_send_data.size() == 0)
2866 {
2867 flat_packed_double_send_data.resize(1);
2868 }
2869
2870 // Vector where to receive the info.
2871 Vector<double> flat_packed_double_receive_data(root_ndtotal_data_receive);
2872 if (my_rank != root_processor)
2873 {
2874 // Create at least one entry so we don't get a seg fault below
2875 if (flat_packed_double_receive_data.size() == 0)
2876 {
2877 flat_packed_double_receive_data.resize(1);
2878 }
2879 }
2880
2881 MPI_Gatherv(&flat_packed_double_send_data[0], // Flat package to
2882 // send info. from
2883 // each processor
2884 nddata_to_send, // Total number of data to send from
2885 // each processor
2886 MPI_DOUBLE,
2887 &flat_packed_double_receive_data[0], // Container where
2888 // to receive the
2889 // info. from all
2890 // the processors
2891 &root_nddata_to_receive[0], // Number of data to receive
2892 // from each processor
2893 &root_doffsets_receive[0], // The offset to store the
2894 // info. from each processor
2895 MPI_DOUBLE,
2896 root_processor, // The processor that receives all the
2897 // info.
2898 comm_pt->mpi_comm());
2899
2900 // Clear the flat package to send
2901 flat_packed_double_send_data.clear();
2902 flat_packed_double_send_data.resize(0);
2903
2904 // The next three containers are only used by the root processor at
2905 // the end of its computations but it is necessary that all the
2906 // processors know them when calling back the info.
2907
2908 // Container that state the initial arclength for each segments
2909 // of each processor
2910 Vector<Vector<double>> root_initial_segment_arclength(nproc);
2911
2912 // Container that state the number of vertices before each segment
2913 // in a given processor
2914 Vector<Vector<unsigned>> root_nvertices_before_segment(nproc);
2915
2916 // The root processor needs to tell the other processor if it was
2917 // necessary to reverse a segment. Each processor should therefore
2918 // invert the face elements that compose every segment that was
2919 // inverted by the root processor
2920 Vector<Vector<unsigned>> root_segment_inverted(nproc);
2921
2922 // Used to store the accumulated arclength, used at the end of
2923 // communications to store the total arclength
2924 double root_accumulated_arclength = 0.0;
2925
2926 // Store the accumulated number of vertices, it means the total number
2927 // of vertices before each segment (counter)
2928 unsigned root_accumulated_vertices_before_segment = 0;
2929
2930 // The root processor is in charge of performing the connections
2931 // of the segments that define the complete boundary
2932 if (my_rank == root_processor)
2933 {
2934 // From the flat packaged received data re-create the data
2935 // structures storing the info. regarding the connectivity of the
2936 // segments, the number of vertices per segment and the local
2937 // arclength of each segment
2938
2939 // Stores the "rank" of the processor to the left of each segment,
2940 // zero if there is no processor to the left which states that the
2941 // segment is the first one on the boundary
2942 Vector<Vector<unsigned>> root_left_processor_plus_one(nproc);
2943
2944 // Stores the "rank" of the processor to the right of each segment,
2945 // zero if there is no processor to the right which states that the
2946 // segment is the last one on the boundary
2947 Vector<Vector<unsigned>> root_right_processor_plus_one(nproc);
2948
2949 // The id. of the halo element to the left of the segment, note that
2950 // this info. is not necessary if there is no processor to the left
2951 // of the segment or if the processor has no info about the boundary
2952 Vector<Vector<unsigned>> root_left_halo_element(nproc);
2953
2954 // The id. of the halo element to the right of the segment, note
2955 // that this info. is not necessary if there is no processor to
2956 // the right of the segment or if the processor has no info about
2957 // the boundary
2958 Vector<Vector<unsigned>> root_right_halo_element(nproc);
2959
2960 // The id. of the haloed element to the left of the segment, note
2961 // that this info. is not necessary if there is no processor to
2962 // the left of the segment or if the processor has no info about
2963 // the boundary
2964 Vector<Vector<unsigned>> root_left_haloed_element(nproc);
2965
2966 // The id. of the haloed element to the right of the segment, note
2967 // that this info. is not necessary if there is no processor to the
2968 // right of the segment or if the processor has no info about the
2969 // boundary
2970 Vector<Vector<unsigned>> root_right_haloed_element(nproc);
2971
2972 // The number of vertices per segment in each processor
2973 Vector<Vector<unsigned>> root_nvertices_per_segment(nproc);
2974
2975 // The arclength of each of the segments in the processors
2976 Vector<Vector<double>> root_segment_arclength(nproc);
2977
2978 unsigned ucounter = 0;
2979 unsigned dcounter = 0;
2980 for (unsigned ip = 0; ip < nproc; ip++)
2981 {
2982 // Get the number of segments in the current processor
2983 const unsigned nsegs_iproc = root_nsegments_per_processor[ip];
2984
2985 root_left_processor_plus_one[ip].resize(nsegs_iproc);
2986 root_right_processor_plus_one[ip].resize(nsegs_iproc);
2987 root_left_halo_element[ip].resize(nsegs_iproc);
2988 root_right_halo_element[ip].resize(nsegs_iproc);
2989 root_left_haloed_element[ip].resize(nsegs_iproc);
2990 root_right_haloed_element[ip].resize(nsegs_iproc);
2991
2992 // Additional info.
2993 root_nvertices_per_segment[ip].resize(nsegs_iproc);
2994 root_segment_arclength[ip].resize(nsegs_iproc);
2995 root_segment_inverted[ip].resize(nsegs_iproc);
2996
2997 // Extract the info. from the BIG package received from all
2998 // processors
2999 for (unsigned is = 0; is < nsegs_iproc; is++)
3000 {
3001 // ------ The flat unsigned package ------
3002 root_left_processor_plus_one[ip][is] =
3003 flat_packed_unsigned_receive_data[ucounter++];
3004 root_right_processor_plus_one[ip][is] =
3005 flat_packed_unsigned_receive_data[ucounter++];
3006 root_left_halo_element[ip][is] =
3007 flat_packed_unsigned_receive_data[ucounter++];
3008 root_right_halo_element[ip][is] =
3009 flat_packed_unsigned_receive_data[ucounter++];
3010 root_left_haloed_element[ip][is] =
3011 flat_packed_unsigned_receive_data[ucounter++];
3012 root_right_haloed_element[ip][is] =
3013 flat_packed_unsigned_receive_data[ucounter++];
3014 root_nvertices_per_segment[ip][is] =
3015 flat_packed_unsigned_receive_data[ucounter++];
3016
3017 // ------ The flat double package ------
3018 root_segment_arclength[ip][is] =
3019 flat_packed_double_receive_data[dcounter++];
3020 } // for (is < nsegs_iproc)
3021 } // for (ip < nproc)
3022
3023 // Now the root processor has all the info. to find out the
3024 // CONNECTIVITY of the segments in each processor
3025
3026 // Container that stores the info. related with the connectivity
3027 // of the segments of each processor
3028 Vector<Vector<int>> left_connected_segment_plus_one(nproc);
3029 Vector<Vector<int>> right_connected_segment_plus_one(nproc);
3030 for (unsigned ip = 0; ip < nproc; ip++)
3031 {
3032 const unsigned nsegs_iproc = root_nsegments_per_processor[ip];
3033 left_connected_segment_plus_one[ip].resize(nsegs_iproc, -1);
3034 right_connected_segment_plus_one[ip].resize(nsegs_iproc, -1);
3035 } // for (ip < nprocs)
3036
3037 // In charge of storing the connectivity of the segments, the pair
3038 // indicates the processor and the segment number
3039 std::list<std::pair<unsigned, unsigned>> proc_seg_connectivity;
3040 proc_seg_connectivity.clear();
3041
3042 // Done segments on processor
3043 std::map<std::pair<unsigned, unsigned>, bool> done_segment;
3044
3045 // Take the first segment of the first processor with segments and
3046 // add it to the list of segments
3047 unsigned left_proc = 0;
3048 unsigned right_proc = 0;
3049 unsigned left_seg = 0;
3050 unsigned right_seg = 0;
3051 for (unsigned ip = 0; ip < nproc; ip++)
3052 {
3053 const unsigned nsegs_iproc = root_nsegments_per_processor[ip];
3054 if (nsegs_iproc > 0)
3055 {
3056 right_proc = left_proc = ip;
3057 right_seg = left_seg = 0;
3058 break; // Break because it is the first processor with at
3059 // least one segment
3060 }
3061 } // for (ip < nproc)
3062
3063 // ... and add it to the list of segments
3064 std::pair<unsigned, unsigned> add_segment =
3065 std::make_pair(left_proc, left_seg);
3066 done_segment[add_segment] = true;
3067 proc_seg_connectivity.push_back(add_segment);
3068
3069 // Flags to indicate when a segment was added to the left or right
3070 // of the current list of segments
3071 bool added_segment_to_the_left = false;
3072 bool added_segment_to_the_right = false;
3073
3074 do // while(added_segment_to_the_left || added_segment_to_the_right)
3075 {
3076 // Read the left-most processor and segment in the list
3077 std::pair<unsigned, unsigned> left_pair = proc_seg_connectivity.front();
3078 left_proc = left_pair.first;
3079 left_seg = left_pair.second;
3080
3081 // Get the processor number to the left of the left-most
3082 // segment in the list
3083 const unsigned new_left_proc =
3084 root_left_processor_plus_one[left_proc][left_seg];
3085
3086 if (new_left_proc != 0)
3087 {
3088 // Initialise flag
3089 added_segment_to_the_left = false;
3090 // Get the left halo element id
3091 const unsigned left_halo_id =
3092 root_left_halo_element[left_proc][left_seg];
3093
3094 // Get the left haloed element id
3095 const unsigned left_haloed_id =
3096 root_left_haloed_element[left_proc][left_seg];
3097
3098 // Go through the segments on the new left processor and look
3099 // for the corresponding left_halo_id in the haloed_ids
3100 const unsigned nsegs_new_left_proc =
3101 root_nsegments_per_processor[new_left_proc - 1];
3102
3103 for (unsigned ils = 0; ils < nsegs_new_left_proc; ils++)
3104 {
3105 std::pair<unsigned, unsigned> candidate_seg =
3106 std::make_pair(new_left_proc - 1, ils);
3107
3108 // Check that the segment has not been already added
3109 if (!done_segment[candidate_seg])
3110 {
3111 // Only consider the segments on new left processor which
3112 // right processor is the current one (left_proc)
3113 const unsigned right_proc_of_new_left_proc =
3114 root_right_processor_plus_one[new_left_proc - 1][ils];
3115 // Also get the left_proc_of_new_left_proc (in case that it
3116 // be necessary to invert the segment)
3117 const unsigned left_proc_of_new_left_proc =
3118 root_left_processor_plus_one[new_left_proc - 1][ils];
3119 // Check the not inverted case (to the left and not
3120 // inverted)
3121 if (right_proc_of_new_left_proc != 0 &&
3122 right_proc_of_new_left_proc - 1 == left_proc)
3123 {
3124 // Get the haloed/haloed element id of the current segment
3125 // in the new left processor and compare it to the
3126 // halo/haloed element id of the left_processor
3127 const unsigned right_halo_id =
3128 root_right_halo_element[new_left_proc - 1][ils];
3129 const unsigned right_haloed_id =
3130 root_right_haloed_element[new_left_proc - 1][ils];
3131 if (left_halo_id == right_haloed_id &&
3132 left_haloed_id == right_halo_id)
3133 {
3134 // We have a match of the segments (store the segment
3135 // number plus one on the processor to the left)
3136 left_connected_segment_plus_one[left_proc][left_seg] =
3137 ils + 1;
3138 // Add the pair to the connectivity list
3139 proc_seg_connectivity.push_front(candidate_seg);
3140 added_segment_to_the_left = true;
3141 break;
3142 }
3143 } // if (right_proc_of_new_left_proc-1 == left_proc)
3144
3145 // Check the inverted case (to the left and inverted)
3146 if (left_proc_of_new_left_proc != 0 &&
3147 left_proc_of_new_left_proc - 1 == left_proc)
3148 {
3149 // Get the haloed element id of the current segment
3150 // (inverted version) in the new left processor and
3151 // compare it to the halo element id of the left_processor
3152 const unsigned inv_left_halo_id =
3153 root_left_halo_element[new_left_proc - 1][ils];
3154 const unsigned inv_left_haloed_id =
3155 root_left_haloed_element[new_left_proc - 1][ils];
3156 if (left_halo_id == inv_left_haloed_id &&
3157 left_haloed_id == inv_left_halo_id)
3158 {
3159 // We have a match of the segments (store the segment
3160 // number plus one on the processor to the left)
3161 left_connected_segment_plus_one[left_proc][left_seg] =
3162 ils + 1;
3163 // Add the pair to the connectivity list
3164 proc_seg_connectivity.push_front(candidate_seg);
3165
3166 // In addition to the connectivity we need to invert the
3167 // segment (the information)
3168 const unsigned tmp_proc =
3169 root_left_processor_plus_one[new_left_proc - 1][ils];
3170 const unsigned tmp_halo =
3171 root_left_halo_element[new_left_proc - 1][ils];
3172 const unsigned tmp_haloed =
3173 root_left_haloed_element[new_left_proc - 1][ils];
3174
3175 root_left_processor_plus_one[new_left_proc - 1][ils] =
3176 root_right_processor_plus_one[new_left_proc - 1][ils];
3177 root_left_halo_element[new_left_proc - 1][ils] =
3178 root_right_halo_element[new_left_proc - 1][ils];
3179 root_left_haloed_element[new_left_proc - 1][ils] =
3180 root_right_haloed_element[new_left_proc - 1][ils];
3181
3182 root_right_processor_plus_one[new_left_proc - 1][ils] =
3183 tmp_proc;
3184 root_right_halo_element[new_left_proc - 1][ils] = tmp_halo;
3185 root_right_haloed_element[new_left_proc - 1][ils] =
3186 tmp_haloed;
3187
3188 // ... and mark the segment as inverted in the root
3189 // processor to inform back to the owner processor
3190 root_segment_inverted[new_left_proc - 1][ils] = 1;
3191
3192 added_segment_to_the_left = true;
3193 break;
3194 }
3195 } // if (left_proc_of_new_left_proc-1 == left_proc)
3196 } // if (!done_segment[candidate_segment])
3197 } // for (ils < nsegs_new_left_proc)
3198
3199#ifdef PARANOID
3200 if (!added_segment_to_the_left)
3201 {
3202 std::ostringstream error_message;
3203 error_message
3204 << "The corresponding processor and segment to the left of "
3205 << "the current left\nmost segment was not found\n";
3206 throw OomphLibError(error_message.str(),
3207 "TriangleMesh::compute_boundary_segments_"
3208 "connectivity_and_initial_zeta_values()",
3209 OOMPH_EXCEPTION_LOCATION);
3210 }
3211#endif
3212 } // if (new_left_proc != 0)
3213 else
3214 {
3215 // No more segments to the left
3216 added_segment_to_the_left = false;
3217 }
3218
3219 // Read the info. of the right processor and the right segment
3220 std::pair<unsigned, unsigned> right_pair = proc_seg_connectivity.back();
3221 right_proc = right_pair.first;
3222 right_seg = right_pair.second;
3223
3224 // Get the processor number to the right of the right-most
3225 // segment in the list
3226 const unsigned new_right_proc =
3227 root_right_processor_plus_one[right_proc][right_seg];
3228
3229 if (new_right_proc != 0)
3230 {
3231 // Initialise flag
3232 added_segment_to_the_right = false;
3233 // Get the right halo element id
3234 const unsigned right_halo_id =
3235 root_right_halo_element[right_proc][right_seg];
3236
3237 // Get the right halo element id
3238 const unsigned right_haloed_id =
3239 root_right_haloed_element[right_proc][right_seg];
3240
3241 // Go through the segments on the new right processor and look
3242 // for the corresponding right_halo_id in the haloed_ids
3243 const unsigned nsegs_new_right_proc =
3244 root_nsegments_per_processor[new_right_proc - 1];
3245
3246 for (unsigned irs = 0; irs < nsegs_new_right_proc; irs++)
3247 {
3248 std::pair<unsigned, unsigned> candidate_seg =
3249 std::make_pair(new_right_proc - 1, irs);
3250
3251 // Check that the segment has not been already added
3252 if (!done_segment[candidate_seg])
3253 {
3254 // Only consider the segments on new right processor which
3255 // left processor is the current one (right_proc)
3256 const unsigned left_proc_of_new_right_proc =
3257 root_left_processor_plus_one[new_right_proc - 1][irs];
3258 // Also get the right_proc_of_new_right_proc (in case
3259 // that it be necessary to invert the segment)
3260 const unsigned right_proc_of_new_right_proc =
3261 root_right_processor_plus_one[new_right_proc - 1][irs];
3262 // Check the not inverted case (to the right and not
3263 // inverted)
3264 if (left_proc_of_new_right_proc != 0 &&
3265 left_proc_of_new_right_proc - 1 == right_proc)
3266 {
3267 // Get the haloed element id of the current segment in the
3268 // new right processor and compare it to the halo element
3269 // id of the right_processor
3270 const unsigned left_halo_id =
3271 root_left_halo_element[new_right_proc - 1][irs];
3272 const unsigned left_haloed_id =
3273 root_left_haloed_element[new_right_proc - 1][irs];
3274
3275 if (right_halo_id == left_haloed_id &&
3276 right_haloed_id == left_halo_id)
3277 {
3278 // We have a match of the segments (store the segment
3279 // number plus one on the processor to the right)
3280 right_connected_segment_plus_one[right_proc][right_seg] =
3281 irs + 1;
3282 // Add the connectivity information to the list
3283 proc_seg_connectivity.push_back(candidate_seg);
3284 added_segment_to_the_right = true;
3285 break;
3286 }
3287 } // if (left_proc_of_new_right_proc-1 == right_proc)
3288
3289 // Check the inverted case (to the right and inverted)
3290 if (right_proc_of_new_right_proc != 0 &&
3291 right_proc_of_new_right_proc - 1 == right_proc)
3292 {
3293 // Get the haloed element id of the current segment
3294 // (inverted version) in the new right processor and
3295 // compare it to the halo element id of the
3296 // right_processor
3297 const unsigned inv_right_halo_id =
3298 root_right_halo_element[new_right_proc - 1][irs];
3299 const unsigned inv_right_haloed_id =
3300 root_right_haloed_element[new_right_proc - 1][irs];
3301 if (right_halo_id == inv_right_haloed_id &&
3302 right_haloed_id == inv_right_halo_id)
3303 {
3304 // We have a match of the segments (store the segment
3305 // number plus one on the processor to the right)
3306 right_connected_segment_plus_one[right_proc][right_seg] =
3307 irs + 1;
3308 // Add the connectivity information to the list
3309 proc_seg_connectivity.push_back(candidate_seg);
3310 // In addition to the connectivity we need to invert the
3311 // segment
3312 const unsigned tmp_proc =
3313 root_left_processor_plus_one[new_right_proc - 1][irs];
3314 const unsigned tmp_halo =
3315 root_left_halo_element[new_right_proc - 1][irs];
3316 const unsigned tmp_haloed =
3317 root_left_haloed_element[new_right_proc - 1][irs];
3318
3319 root_left_processor_plus_one[new_right_proc - 1][irs] =
3320 root_right_processor_plus_one[new_right_proc - 1][irs];
3321 root_left_halo_element[new_right_proc - 1][irs] =
3322 root_right_halo_element[new_right_proc - 1][irs];
3323 root_left_haloed_element[new_right_proc - 1][irs] =
3324 root_right_haloed_element[new_right_proc - 1][irs];
3325
3326 root_right_processor_plus_one[new_right_proc - 1][irs] =
3327 tmp_proc;
3328 root_right_halo_element[new_right_proc - 1][irs] = tmp_halo;
3329 root_right_haloed_element[new_right_proc - 1][irs] =
3330 tmp_haloed;
3331
3332 // ... and mark the segment as inverted in the root
3333 // processor to inform back to the owner processor
3334 root_segment_inverted[new_right_proc - 1][irs] = 1;
3335
3336 added_segment_to_the_right = true;
3337 break;
3338 }
3339 } // if (right_proc_of_new_right_proc-1 == right_proc)
3340 } // if (!done_segment[candidate_segment])
3341 } // for (irs < nsegs_new_left_proc)
3342
3343#ifdef PARANOID
3344 if (!added_segment_to_the_right)
3345 {
3346 std::ostringstream error_message;
3347 error_message
3348 << "The corresponding processor and segment to the right of "
3349 << "the current right\nmost segment was not found\n";
3350 throw OomphLibError(error_message.str(),
3351 "TriangleMesh::compute_boundary_segments_"
3352 "connectivity_and_initial_zeta_values()",
3353 OOMPH_EXCEPTION_LOCATION);
3354 }
3355#endif
3356 } // if (new_right_proc != 0)
3357 else
3358 {
3359 // No more segments to the left
3360 added_segment_to_the_right = false;
3361 }
3362
3363 } while (added_segment_to_the_left || added_segment_to_the_right);
3364
3365 // Once we have connected the segments then we can compute the
3366 // initial and final zeta values based on the arclength of each
3367 // individual segment
3368
3369 // Get the total number of segments, which MUST be the same as the
3370 // total number of segments in all processors
3371 const unsigned ntotal_segments = proc_seg_connectivity.size();
3372#ifdef PARANOID
3373 unsigned tmp_total_segments = 0;
3374 for (unsigned ip = 0; ip < nproc; ip++)
3375 {
3376 tmp_total_segments += root_nsegments_per_processor[ip];
3377 }
3378
3379 // Check that the total number of segments in all processors is
3380 // the same as the number of segments that form the boundary
3381 if (ntotal_segments != tmp_total_segments)
3382 {
3383 std::ostringstream error_message;
3384 error_message << "The number of sorted segments (" << ntotal_segments
3385 << ") on "
3386 << "boundary (" << b
3387 << ")\nis different from the total number of "
3388 << "segments (" << tmp_total_segments
3389 << ") in all\nprocessors.\n\n";
3390 throw OomphLibError(error_message.str(),
3391 OOMPH_CURRENT_FUNCTION,
3392 OOMPH_EXCEPTION_LOCATION);
3393 } // if (ntotal_segments!=tmp_total_segments)
3394#endif
3395
3396 // Now that we know the connectivity of the segments we can
3397 // compute the initial arclength of each segment in the
3398 // processors. Additionally we also get the number of vertices
3399 // before each of the segments. Resize the containers considering
3400 // the number of segments in each processor
3401 for (unsigned ip = 0; ip < nproc; ip++)
3402 {
3403 const unsigned nsegs_iproc = root_nsegments_per_processor[ip];
3404 root_initial_segment_arclength[ip].resize(nsegs_iproc);
3405 root_nvertices_before_segment[ip].resize(nsegs_iproc);
3406 }
3407
3408 Vector<double> aux_initial_segment_arclength(ntotal_segments);
3409 Vector<unsigned> aux_nvertices_before_segment(ntotal_segments);
3410
3411 ucounter = 0;
3412 for (std::list<std::pair<unsigned, unsigned>>::iterator it_list =
3413 proc_seg_connectivity.begin();
3414 it_list != proc_seg_connectivity.end();
3415 it_list++)
3416 {
3417 const unsigned iproc = static_cast<unsigned>((*it_list).first);
3418 const unsigned iseg = static_cast<unsigned>((*it_list).second);
3419 const double iseg_arclength = root_segment_arclength[iproc][iseg];
3420 const unsigned iseg_nvertices = root_nvertices_per_segment[iproc][iseg];
3421
3422 aux_initial_segment_arclength[ucounter] = root_accumulated_arclength;
3423 aux_nvertices_before_segment[ucounter] =
3424 root_accumulated_vertices_before_segment;
3425
3426 // Set the initial zeta value for the segment
3427 root_initial_segment_arclength[iproc][iseg] =
3428 root_accumulated_arclength;
3429 // Set the number of vertices before the current segment
3430 root_nvertices_before_segment[iproc][iseg] =
3431 root_accumulated_vertices_before_segment;
3432
3433 // Add the arclength of the segment to the global arclength
3434 root_accumulated_arclength += iseg_arclength;
3435 // Add the number of vertices to the global number of vertices
3436 root_accumulated_vertices_before_segment += iseg_nvertices - 1;
3437
3438 // Increase the counter
3439 ucounter++;
3440 } // for (loop over the sorted segments to assigne initial
3441 // arlength and initial number of vertices)
3442
3443 // Increase by one to get the total number of vertices on the
3444 // boundary
3445 root_accumulated_vertices_before_segment++;
3446
3447 // Get the processors with the initial and final segment.
3448 proc_with_initial_seg = proc_seg_connectivity.front().first;
3449 proc_with_final_seg = proc_seg_connectivity.back().first;
3450 // Also get the corresponding initial and final segment indexes
3451 // (on the initial and final processors)
3452 initial_segment = proc_seg_connectivity.front().second;
3453 final_segment = proc_seg_connectivity.back().second;
3454
3455 } // if (my_rank == root_processor)
3456
3457 // Get the total number of segments
3458 unsigned root_ntotal_segments = 0;
3459 for (unsigned ip = 0; ip < nproc; ip++)
3460 {
3461 root_ntotal_segments += root_nsegments_per_processor[ip];
3462 }
3463
3464 // Package the info. that will be sent to each processor. For the
3465 // unsigned package we send the number of vertices before each
3466 // segment in each processor and whether it was inverted or not
3467 // Package size
3468 const unsigned rspu = 2;
3469 flat_packed_unsigned_send_data.clear();
3470 flat_packed_unsigned_send_data.resize(root_ntotal_segments * rspu);
3471 unsigned ucounter = 0;
3472 // Collect the info. from all the segments in the processors
3473 for (unsigned ip = 0; ip < nproc; ip++)
3474 {
3475 const unsigned nsegs_iproc = root_nsegments_per_processor[ip];
3476 for (unsigned is = 0; is < nsegs_iproc; is++)
3477 {
3478 flat_packed_unsigned_send_data[ucounter++] =
3479 root_nvertices_before_segment[ip][is];
3480 flat_packed_unsigned_send_data[ucounter++] =
3481 root_segment_inverted[ip][is];
3482 } // for (is < nsegs_iproc)
3483 } // for (ip < nproc)
3484
3485 // How many data does the root processor will send to each processor
3486 Vector<int> root_nudata_to_send(nproc, 0);
3487 for (unsigned ip = 0; ip < nproc; ip++)
3488 {
3489 // Get the number of data to send to ip processor
3490 root_nudata_to_send[ip] = root_nsegments_per_processor[ip] * rspu;
3491 }
3492
3493 // Store and compute the offsets for the data sent to each processor
3494 Vector<int> root_uoffsets_send(nproc, 0);
3495 root_uoffsets_send[0] = 0;
3496 for (unsigned ip = 1; ip < nproc; ip++)
3497 {
3498 // Compute the offset to send the values to each processor
3499 root_uoffsets_send[ip] =
3500 root_uoffsets_send[ip - 1] + root_nudata_to_send[ip - 1];
3501 }
3502
3503 // Number of data to receive from root
3504 unsigned nutotal_data_receive = nsegments * rspu;
3505
3506 if (my_rank != root_processor)
3507 {
3508 // Create at least one entry so we don't get a seg fault below
3509 if (flat_packed_unsigned_send_data.size() == 0)
3510 {
3511 flat_packed_unsigned_send_data.resize(1);
3512 }
3513 }
3514
3515 // Clear and resize the vector where to receive the info.
3516 flat_packed_unsigned_receive_data.clear();
3517 flat_packed_unsigned_receive_data.resize(nutotal_data_receive);
3518 // Create at least one entry so we don't get a seg fault below
3519 if (flat_packed_unsigned_receive_data.size() == 0)
3520 {
3521 flat_packed_unsigned_receive_data.resize(1);
3522 }
3523
3524 MPI_Scatterv(&flat_packed_unsigned_send_data[0],
3525 &root_nudata_to_send[0],
3526 &root_uoffsets_send[0],
3527 MPI_UNSIGNED,
3528 &flat_packed_unsigned_receive_data[0],
3529 nutotal_data_receive,
3530 MPI_UNSIGNED,
3531 root_processor,
3532 comm_pt->mpi_comm());
3533
3534 // Package the info. that will be sent to each processor, for the
3535 // double package we send (one data per segment) the initial
3536 // arclength for each segment
3537 const unsigned rspd = 1;
3538 flat_packed_double_send_data.clear();
3539 flat_packed_double_send_data.resize(root_ntotal_segments * rspd);
3540 unsigned dcounter = 0;
3541 // Collect the info. from all the segments in the processors
3542 for (unsigned ip = 0; ip < nproc; ip++)
3543 {
3544 const unsigned nsegs_iproc = root_nsegments_per_processor[ip];
3545 for (unsigned is = 0; is < nsegs_iproc; is++)
3546 {
3547 flat_packed_double_send_data[dcounter++] =
3548 root_initial_segment_arclength[ip][is];
3549 }
3550 }
3551
3552 // How many data does the root processor will send to each processor
3553 Vector<int> root_nddata_to_send(nproc, 0);
3554 for (unsigned ip = 0; ip < nproc; ip++)
3555 {
3556 // Number of data send to ip processor
3557 root_nddata_to_send[ip] = root_nsegments_per_processor[ip] * rspd;
3558 }
3559
3560 // Store and compute the offsets for the data sent to each processor
3561 Vector<int> root_doffsets_send(nproc, 0);
3562 root_doffsets_send[0] = 0;
3563 for (unsigned ip = 1; ip < nproc; ip++)
3564 {
3565 // Compute the offset to send the values to each processor
3566 root_doffsets_send[ip] =
3567 root_doffsets_send[ip - 1] + root_nddata_to_send[ip - 1];
3568 }
3569
3570 // Number of double data to receive from root
3571 unsigned ndtotal_data_receive = nsegments * rspd;
3572
3573 if (my_rank != root_processor)
3574 {
3575 // Create at least one entry so we don't get a seg fault below
3576 if (flat_packed_double_send_data.size() == 0)
3577 {
3578 flat_packed_double_send_data.resize(1);
3579 }
3580 }
3581
3582 // Clear and resize the vector where to receive the info.
3583 flat_packed_double_receive_data.clear();
3584 flat_packed_double_receive_data.resize(ndtotal_data_receive);
3585 // Create at least one entry so we don't get a seg fault below
3586 if (flat_packed_double_receive_data.size() == 0)
3587 {
3588 flat_packed_double_receive_data.resize(1);
3589 }
3590
3591 MPI_Scatterv(&flat_packed_double_send_data[0],
3592 &root_nddata_to_send[0],
3593 &root_doffsets_send[0],
3594 MPI_DOUBLE,
3595 &flat_packed_double_receive_data[0],
3596 ndtotal_data_receive,
3597 MPI_DOUBLE,
3598 root_processor,
3599 comm_pt->mpi_comm());
3600
3601 // Read if the segments need to be inverted and read the initial
3602 // arclengths
3603 ucounter = 0;
3604 dcounter = 0;
3605
3606 // Read the info. from the flat package and store it in their
3607 // corresponding containers
3608 for (unsigned is = 0; is < nsegments; is++)
3609 {
3610 // The flat unsigned package
3611 nvertices_before_segment[is] =
3612 flat_packed_unsigned_receive_data[ucounter++];
3613 // The segment inverted flag
3614 segment_inverted[is] = flat_packed_unsigned_receive_data[ucounter++];
3615 // The flat double package
3616 initial_segment_arclength[is] =
3617 flat_packed_double_receive_data[dcounter++];
3618 } // for (is < nsegments)
3619
3620 // Perform two additional communications to get the total number of
3621 // vertices, the processors with the initial and final segments, the
3622 // corresponding initial and final segments ...
3623 const unsigned numore_info = 5;
3624 Vector<unsigned> flat_package_unsigned_more_info(numore_info);
3625 // Prepare the info ...
3626 flat_package_unsigned_more_info[0] =
3627 root_accumulated_vertices_before_segment;
3628 flat_package_unsigned_more_info[1] = proc_with_initial_seg;
3629 flat_package_unsigned_more_info[2] = proc_with_final_seg;
3630 flat_package_unsigned_more_info[3] = initial_segment;
3631 flat_package_unsigned_more_info[4] = final_segment;
3632
3633 // Send the info. to all processors
3634 MPI_Bcast(&flat_package_unsigned_more_info[0],
3635 numore_info,
3636 MPI_UNSIGNED,
3637 root_processor,
3638 comm_pt->mpi_comm());
3639
3640 // ... and store the info. in the proper containers
3641 root_accumulated_vertices_before_segment =
3642 flat_package_unsigned_more_info[0];
3643 proc_with_initial_seg = flat_package_unsigned_more_info[1];
3644 proc_with_final_seg = flat_package_unsigned_more_info[2];
3645 initial_segment = flat_package_unsigned_more_info[3];
3646 final_segment = flat_package_unsigned_more_info[4];
3647
3648 // Do the same for the maximum zeta value
3649 MPI_Bcast(&root_accumulated_arclength,
3650 1,
3651 MPI_DOUBLE,
3652 root_processor,
3653 comm_pt->mpi_comm());
3654
3655 // -----------------------------------------------------------------
3656 // Clear the storage to store the data that will be used by the
3657 // setup boundary coordinates method, if we do not perform the
3658 // cleaning then previous data from previous iterations will remain
3659 // there
3660 // -----------------------------------------------------------------
3661 // The info. for the boundary
3662 Boundary_initial_coordinate[b].clear();
3663 Boundary_final_coordinate[b].clear();
3664
3665 Boundary_initial_zeta_coordinate[b].clear();
3666 Boundary_final_zeta_coordinate[b].clear();
3667
3668 // The info. for the segments
3669 Boundary_segment_inverted[b].clear();
3670 Boundary_segment_initial_coordinate[b].clear();
3671 Boundary_segment_final_coordinate[b].clear();
3672
3673 Boundary_segment_initial_zeta[b].clear();
3674 Boundary_segment_final_zeta[b].clear();
3675
3676 Boundary_segment_initial_arclength[b].clear();
3677 Boundary_segment_final_arclength[b].clear();
3678
3679 // Now copy all the info. to the containers to be sent to any other
3680 // mesh (in the adaptation method)
3681 for (unsigned is = 0; is < nsegments; is++)
3682 {
3683 // At this point we can get the initial and final coordinates for
3684 // each segment
3685 Vector<double> first_seg_coord(2);
3686 Vector<double> last_seg_coord(2);
3687
3688 // In order to get the first and last coordinates of each segment we
3689 // first need to identify the first and last nonhalo element of each
3690 // segment, and then get the first and last node of the segment
3691
3692 // Get the first nonhalo face element on the segment
3693 FiniteElement* first_seg_ele_pt =
3694 segment_sorted_nonhalo_ele_pt[is].front();
3695
3696#ifdef PARANOID
3697 // Check if the face element is nonhalo, it shouldn't, but better
3698 // check
3699 if (first_seg_ele_pt->is_halo())
3700 {
3701 std::ostringstream error_message;
3702 error_message << "The first face element in the (" << is
3703 << ")-th segment is halo\n";
3704 throw OomphLibError(error_message.str(),
3705 "TriangleMesh::compute_boundary_segments_"
3706 "connectivity_and_initial_zeta_values()",
3707 OOMPH_EXCEPTION_LOCATION);
3708 } // if (tmp_first_bulk_ele_pt->is_halo())
3709#endif
3710
3711 // Number of nodes
3712 const unsigned nnod = first_seg_ele_pt->nnode();
3713
3714 // Get the first node of the current segment
3715 Node* first_seg_node_pt = first_seg_ele_pt->node_pt(0);
3716 if (is_inverted[first_seg_ele_pt])
3717 {
3718 first_seg_node_pt = first_seg_ele_pt->node_pt(nnod - 1);
3719 }
3720
3721 // Get the last nonhalo face element on the segment
3722 FiniteElement* last_seg_ele_pt = segment_sorted_nonhalo_ele_pt[is].back();
3723
3724#ifdef PARANOID
3725 // Check if the face element is nonhalo, it shouldn't, but better
3726 // check
3727 if (last_seg_ele_pt->is_halo())
3728 {
3729 std::ostringstream error_message;
3730 error_message << "The last face element in the (" << is
3731 << ")-th segment is halo\n";
3732 throw OomphLibError(error_message.str(),
3733 "TriangleMesh::compute_boundary_segments_"
3734 "connectivity_and_initial_zeta_values()",
3735 OOMPH_EXCEPTION_LOCATION);
3736 } // if (tmp_first_bulk_ele_pt->is_halo())
3737#endif
3738
3739 // Get the last node of the current segment
3740 Node* last_seg_node_pt = last_seg_ele_pt->node_pt(nnod - 1);
3741 if (is_inverted[last_seg_ele_pt])
3742 {
3743 last_seg_node_pt = last_seg_ele_pt->node_pt(0);
3744 }
3745
3746 // Get the coordinates for the first and last segment's node
3747 for (unsigned i = 0; i < 2; i++)
3748 {
3749 first_seg_coord[i] = first_seg_node_pt->x(i);
3750 last_seg_coord[i] = last_seg_node_pt->x(i);
3751 }
3752
3753 // -----------------------------------------------------------------
3754 // Copy the info. if the segment is inverted
3755 Boundary_segment_inverted[b].push_back(segment_inverted[is]);
3756
3757 // Check if the segment is inverted, if that is the case then invert
3758 // the first and last seg. coordinates
3759 if (!segment_inverted[is])
3760 {
3761 // Store the initial and final coordinates that will help to
3762 // identify the segments in the new meshes created from this one
3763 Boundary_segment_initial_coordinate[b].push_back(first_seg_coord);
3764 Boundary_segment_final_coordinate[b].push_back(last_seg_coord);
3765 }
3766 else
3767 {
3768 // Store the initial and final coordinates that will help to
3769 // identify the segments in the new meshes created from this one
3770 // Invert the initial and final coordinates
3771 Boundary_segment_initial_coordinate[b].push_back(last_seg_coord);
3772 Boundary_segment_final_coordinate[b].push_back(first_seg_coord);
3773 }
3774
3775 // Now assign initial and final zeta boundary coordinates for each
3776 // segment
3777 // -----------------------------------------------------------------
3778 // If there is a geom object then
3779 if (boundary_geom_object_pt(b) != 0)
3780 {
3781 // Store the initial and final zeta for the current segments (we
3782 // got this when we assigned arclength to the segments in the
3783 // current processor)
3784 if (segment_inverted[is])
3785 {
3786 Boundary_segment_initial_zeta[b].push_back(final_zeta_segment[is]);
3787 Boundary_segment_final_zeta[b].push_back(initial_zeta_segment[is]);
3788 }
3789 else
3790 {
3791 Boundary_segment_initial_zeta[b].push_back(initial_zeta_segment[is]);
3792 Boundary_segment_final_zeta[b].push_back(final_zeta_segment[is]);
3793 }
3794 } // if (boundary_geom_object_pt(b)!=0)
3795 else
3796 {
3797 // Store the initial arclength and vertices number for the
3798 // current segment
3799 Boundary_segment_initial_arclength[b].push_back(
3800 initial_segment_arclength[is]);
3801
3802 Boundary_segment_final_arclength[b].push_back(
3803 initial_segment_arclength[is] + segment_arclength[is]);
3804
3805 } // else if (boundary_geom_object_pt(b)!=0)
3806
3807 } // // for (is < nsegments)
3808
3809 // Get the number of segments from the sets of nodes
3810#ifdef PARANOID
3811 if (segment_all_nodes_pt.size() != nsegments)
3812 {
3813 std::ostringstream error_message;
3814 error_message << "The number of segments (" << nsegments
3815 << ") and the number of "
3816 << "set of nodes (" << segment_all_nodes_pt.size()
3817 << ") representing\n"
3818 << "the\nsegments is different!!!\n\n";
3819 throw OomphLibError(error_message.str(),
3820 "TriangleMesh::compute_boundary_segments_"
3821 "connectivity_and_initial_zeta_values()",
3822 OOMPH_EXCEPTION_LOCATION);
3823 }
3824#endif
3825
3826 // The nodes have been assigned arc-length coordinates from one end
3827 // or the other of the connected segment.
3828
3829 // -----------------------------------------------------------------
3830 // If mesh is distributed get the info. regarding the initial and
3831 // final nodes coordinates on the boundary, same as the zeta
3832 // boundary values for those nodes
3833
3834 // Storage for the coordinates of the first and last nodes on the
3835 // boundary
3836 Vector<double> first_coordinate(2);
3837 Vector<double> last_coordinate(2);
3838
3839 // Storage for the zeta coordinate of the first and last nodes on
3840 // the boundary
3841 Vector<double> first_node_zeta_coordinate(1, 0.0);
3842 Vector<double> last_node_zeta_coordinate(1, 0.0);
3843
3844 // Send three data to all processors, the x[0], x[1] coordinate and
3845 // the zeta coordinate
3846 const unsigned ndtotal_data = 3;
3847 Vector<double> flat_packed_double_data_initial_seg(ndtotal_data);
3848
3849 // If the mesh is distributed then check if this processor has the
3850 // initial segment
3851 if (my_rank == proc_with_initial_seg)
3852 {
3853 // Stores the firts element of the segment
3854 FiniteElement* first_ele_pt = 0;
3855 // Stores the first node of the boundary
3856 Node* first_node_pt = 0;
3857 // Check if the segment is inverted
3858 if (!segment_inverted[initial_segment])
3859 {
3860 // Get access to the first element on the segment marked as
3861 // initial
3862 first_ele_pt = segment_sorted_ele_pt[initial_segment].front();
3863
3864 // Number of nodes
3865 const unsigned nnod = first_ele_pt->nnode();
3866
3867 // Get the first node of the current segment
3868 first_node_pt = first_ele_pt->node_pt(0);
3869 if (is_inverted[first_ele_pt])
3870 {
3871 first_node_pt = first_ele_pt->node_pt(nnod - 1);
3872 }
3873 } // if (!segment_inverted[initial_segment])
3874 else
3875 {
3876 // Get access to the first element on the segment marked as
3877 // initial
3878 first_ele_pt = segment_sorted_ele_pt[initial_segment].back();
3879
3880 // Number of nodes
3881 const unsigned nnod = first_ele_pt->nnode();
3882
3883 // Get the first node of the current segment
3884 first_node_pt = first_ele_pt->node_pt(nnod - 1);
3885 if (is_inverted[first_ele_pt])
3886 {
3887 first_node_pt = first_ele_pt->node_pt(0);
3888 }
3889 } // else if (!segment_inverted[initial_segment])
3890
3891 // Get the coordinates for the first node
3892 for (unsigned i = 0; i < 2; i++)
3893 {
3894 flat_packed_double_data_initial_seg[i] = first_node_pt->x(i);
3895 }
3896
3897 // Get the zeta coordinates for the first node
3898 Vector<double> tmp_zeta(1);
3899 first_node_pt->get_coordinates_on_boundary(b, tmp_zeta);
3900
3901 // If there is a geometric object associated to the boundary then
3902 // further process is necessary
3903 if (this->boundary_geom_object_pt(b) != 0)
3904 {
3905 // tmp_zeta[0] = this->boundary_coordinate_limits(b)[0];
3906 }
3907 else
3908 {
3909 // Check if the initial boundary coordinate is different from
3910 // zero, if that is the case then we need to set it to zero
3911 if (tmp_zeta[0] >= 1.0e-14)
3912 {
3913 tmp_zeta[0] = 0;
3914 }
3915 } // if (this->boundary_geom_object_pt(b)!=0)
3916
3917 // Store the initial zeta value
3918 flat_packed_double_data_initial_seg[2] = tmp_zeta[0];
3919
3920 } // if (my_rank == proc_with_initial_seg)
3921
3922 // All processor receive the info. from the processor that has the
3923 // initial segment
3924 MPI_Bcast(&flat_packed_double_data_initial_seg[0],
3925 ndtotal_data,
3926 MPI_DOUBLE,
3927 proc_with_initial_seg,
3928 comm_pt->mpi_comm());
3929
3930 // ... and all processor put that info. into the appropriate
3931 // storages
3932 for (unsigned i = 0; i < 2; i++)
3933 {
3934 first_coordinate[i] = flat_packed_double_data_initial_seg[i];
3935 }
3936 first_node_zeta_coordinate[0] = flat_packed_double_data_initial_seg[2];
3937
3938 // -----------------------------------------------------------------
3939 // Send three data to all processors, the x[0], x[1] coordinate and
3940 // the zeta coordinate
3941 Vector<double> flat_packed_double_data_final_seg(ndtotal_data);
3942
3943 // If the mesh is distributed then check if this processor has the
3944 // final segment
3945 if (my_rank == proc_with_final_seg)
3946 {
3947 // Get access to the last element on the segment
3948 FiniteElement* last_ele_pt = 0;
3949
3950 // Get the last node of the current segment
3951 Node* last_node_pt = 0;
3952
3953 // Check if the segment is inverted
3954 if (!segment_inverted[final_segment])
3955 {
3956 // Get access to the last element on the segment marked as
3957 // final
3958 last_ele_pt = segment_sorted_ele_pt[final_segment].back();
3959
3960 // Number of nodes
3961 const unsigned nnod = last_ele_pt->nnode();
3962
3963 // Get the last node of the current segment
3964 last_node_pt = last_ele_pt->node_pt(nnod - 1);
3965 if (is_inverted[last_ele_pt])
3966 {
3967 last_node_pt = last_ele_pt->node_pt(0);
3968 }
3969 } // if (!segment_inverted[final_segment])
3970 else
3971 {
3972 // Get access to the first element on the segment marked as
3973 // initial
3974 last_ele_pt = segment_sorted_ele_pt[final_segment].front();
3975
3976 // Number of nodes
3977 const unsigned nnod = last_ele_pt->nnode();
3978
3979 // Get the first node of the current segment
3980 last_node_pt = last_ele_pt->node_pt(0);
3981 if (is_inverted[last_ele_pt])
3982 {
3983 last_node_pt = last_ele_pt->node_pt(nnod - 1);
3984 }
3985 } // if (!segment_inverted[final_segment])
3986
3987 // Get the coordinates for the last node
3988 for (unsigned i = 0; i < 2; i++)
3989 {
3990 flat_packed_double_data_final_seg[i] = last_node_pt->x(i);
3991 }
3992
3993 // Get the zeta coordinates for the last node
3994 Vector<double> tmp_zeta(1);
3995 last_node_pt->get_coordinates_on_boundary(b, tmp_zeta);
3996
3997 // If there is not a geometric object associated to the boundary
3998 // then further process is required
3999 if (this->boundary_geom_object_pt(b) != 0)
4000 {
4001 // Do nothing
4002 } // if (this->boundary_geom_object_pt(b)!=0)
4003 else
4004 {
4005 // Check if the final boundary coordinate is different from
4006 // the boundary arclength, if that is the case then we need
4007 // to set it to the accumulated arclength
4008 if (std::fabs(tmp_zeta[0] - root_accumulated_arclength) >= 1.0e-14)
4009 {
4010 tmp_zeta[0] = root_accumulated_arclength;
4011 }
4012 } // else if (this->boundary_geom_object_pt(b)!=0)
4013
4014 // Store the final zeta value
4015 flat_packed_double_data_final_seg[2] = tmp_zeta[0];
4016
4017 } // if (my_rank == proc_with_final_seg)
4018
4019 // All processor receive the info. from the processor that has the
4020 // final segment
4021 MPI_Bcast(&flat_packed_double_data_final_seg[0],
4022 ndtotal_data,
4023 MPI_DOUBLE,
4024 proc_with_final_seg,
4025 comm_pt->mpi_comm());
4026
4027 // All processor receive the info. from the processor that has the
4028 // final segment
4029 for (unsigned i = 0; i < 2; i++)
4030 {
4031 last_coordinate[i] = flat_packed_double_data_final_seg[i];
4032 }
4033 last_node_zeta_coordinate[0] = flat_packed_double_data_final_seg[2];
4034
4035 // -----------------------------------------------------------------
4036 // Copy the values to the permanent storage
4037 Boundary_initial_coordinate[b] = first_coordinate;
4038 Boundary_final_coordinate[b] = last_coordinate;
4039
4040 Boundary_initial_zeta_coordinate[b] = first_node_zeta_coordinate;
4041 Boundary_final_zeta_coordinate[b] = last_node_zeta_coordinate;
4042
4043 // If we are dealing with an internal boundary then re-assign the
4044 // initial and final zeta values for the segments
4045 if (is_internal_boundary)
4046 {
4047 // Only re-assign zeta values if there are at least one nonhalo
4048 // segment, if all the possible segments are halo then the
4049 // synchronisation method will be in charge of assigning the
4050 // correct boundary coordinates
4051 if (nsegments > 0)
4052 {
4053 // Call the following method to re-construct the segments but
4054 // using only the nonhalo elements, therefore the boundary
4055 // coordinates need to be re-assigned
4056 re_assign_initial_zeta_values_for_internal_boundary(
4057 b, segment_sorted_nonhalo_ele_pt, is_inverted);
4058 }
4059
4060 } // if (is_internal_boundary)
4061
4062 // Now identify the boundary segments
4063 if (nsegments > 0)
4064 {
4065 // Identify the boundary segments in the current mesh
4066 // identify_boundary_segments_and_assign_initial_zeta_values(
4067 // b, all_face_ele_pt, is_internal_boundary, face_to_bulk_element_pt);
4068 identify_boundary_segments_and_assign_initial_zeta_values(b, this);
4069 } // if (nsegments > 0)
4070
4071 // Clean all the created face elements
4072 for (unsigned i = 0; i < n_all_face_ele; i++)
4073 {
4074 delete all_face_ele_pt[i];
4075 all_face_ele_pt[i] = 0;
4076 }
4077 }
4078
4079 //======================================================================
4080 /// Re-assign the boundary segments initial zeta (arclength)
4081 /// for those internal boundaries that were splited during the
4082 /// distribution process. Those boundaries that have one face element
4083 /// at each side of the boundary. Here we create the segments only
4084 /// with the nonhalo elements, therefore the boundary coordinates
4085 /// need to be re-assigned to be passed to the new meshes
4086 //======================================================================
4087 template<class ELEMENT>
4090 const unsigned& b,
4091 Vector<std::list<FiniteElement*>>& old_segment_sorted_ele_pt,
4092 std::map<FiniteElement*, bool>& old_is_inverted)
4093 {
4094 // ------------------------------------------------------------------
4095 // First: Get the face elements associated with the current boundary
4096 // Only include nonhalo face elements
4097 // ------------------------------------------------------------------
4098 // Temporary storage for face elements
4099 Vector<FiniteElement*> face_el_pt;
4100
4101 // Temporary storage for the number of elements adjacent to the
4102 // boundary
4103 unsigned nele = 0;
4104
4105 // Temporary storage for elements adjacent to the boundary that have
4106 // a common edge (related with internal boundaries)
4107 unsigned n_repeated_ele = 0;
4108
4109 const unsigned n_regions = this->nregion();
4110
4111 // Temporary storage for already done nodes
4112 Vector<std::pair<Node*, Node*>> done_nodes_pt;
4113
4114 // If there is more than one region then only use boundary
4115 // coordinates from the bulk side (region 0)
4116 if (n_regions > 1)
4117 {
4118 for (unsigned rr = 0; rr < n_regions; rr++)
4119 {
4120 const unsigned region_id =
4121 static_cast<unsigned>(this->Region_attribute[rr]);
4122
4123 // Loop over all elements on boundaries in region i_r
4124 const unsigned nel_in_region =
4125 this->nboundary_element_in_region(b, region_id);
4126
4127 unsigned nel_repetead_in_region = 0;
4128
4129 // Only bother to do anything else, if there are elements
4130 // associated with the boundary and the current region
4131 if (nel_in_region > 0)
4132 {
4133 bool repeated = false;
4134
4135 // Loop over the bulk elements adjacent to boundary b
4136 for (unsigned e = 0; e < nel_in_region; e++)
4137 {
4138 // Get pointer to the bulk element that is adjacent to
4139 // boundary b
4140 FiniteElement* bulk_elem_pt =
4141 this->boundary_element_in_region_pt(b, region_id, e);
4142
4143 // Remember only work with non halo elements
4144 if (bulk_elem_pt->is_halo())
4145 {
4146 n_repeated_ele++;
4147 continue;
4148 }
4149
4150 // Find the index of the face of element e along boundary b
4151 int face_index =
4152 this->face_index_at_boundary_in_region(b, region_id, e);
4153
4154 // Before adding the new element we need to be sure that the
4155 // edge that this element represent has not been already
4156 // added
4157 FiniteElement* tmp_ele_pt =
4158 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
4159
4160 const unsigned n_nodes = tmp_ele_pt->nnode();
4161
4162 std::pair<Node*, Node*> tmp_pair = std::make_pair(
4163 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
4164
4165 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
4166 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
4167
4168 // Search for repeated nodes
4169 const unsigned repeated_nodes_size = done_nodes_pt.size();
4170 for (unsigned l = 0; l < repeated_nodes_size; l++)
4171 {
4172 if (tmp_pair == done_nodes_pt[l] ||
4173 tmp_pair_inverse == done_nodes_pt[l])
4174 {
4175 nel_repetead_in_region++;
4176 repeated = true;
4177 break;
4178 }
4179 }
4180
4181 // Create new face element
4182 if (!repeated)
4183 {
4184 // Add the pair of nodes (edge) to the node dones
4185 done_nodes_pt.push_back(tmp_pair);
4186 // Add the element to the face elements
4187 face_el_pt.push_back(tmp_ele_pt);
4188 }
4189 else
4190 {
4191 // Clean up
4192 delete tmp_ele_pt;
4193 tmp_ele_pt = 0;
4194 }
4195
4196 // Re-start
4197 repeated = false;
4198
4199 } // for nel
4200
4201 nele += nel_in_region;
4202
4203 n_repeated_ele += nel_repetead_in_region;
4204
4205 } // if (nel_in_region > 0)
4206 } // for (rr < n_regions)
4207 } // if (n_regions > 1)
4208 // Otherwise it's just the normal boundary functions
4209 else
4210 {
4211 // Loop over all elements on boundaries
4212 nele = this->nboundary_element(b);
4213
4214 // Only bother to do anything else, if there are elements
4215 if (nele > 0)
4216 {
4217 // Check for repeated ones
4218 bool repeated = false;
4219
4220 // Loop over the bulk elements adjacent to boundary b
4221 for (unsigned e = 0; e < nele; e++)
4222 {
4223 // Get pointer to the bulk element that is adjacent to
4224 // boundary b
4225 FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
4226
4227 // Skip the halo elements, they are not included
4228 if (bulk_elem_pt->is_halo())
4229 {
4230 n_repeated_ele++;
4231 continue;
4232 }
4233
4234 // Find the index of the face of element e along boundary b
4235 int face_index = this->face_index_at_boundary(b, e);
4236
4237 // Before adding the new element we need to be sure that the
4238 // edge that this element represents has not been already
4239 // added (only applies for internal boundaries)
4240 FiniteElement* tmp_ele_pt =
4241 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
4242
4243 const unsigned n_nodes = tmp_ele_pt->nnode();
4244
4245 std::pair<Node*, Node*> tmp_pair = std::make_pair(
4246 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
4247
4248 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
4249 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
4250
4251 // Search for repeated nodes
4252 const unsigned repeated_nodes_size = done_nodes_pt.size();
4253 for (unsigned l = 0; l < repeated_nodes_size; l++)
4254 {
4255 if (tmp_pair == done_nodes_pt[l] ||
4256 tmp_pair_inverse == done_nodes_pt[l])
4257 {
4258 // Increase the number of repeated elements
4259 n_repeated_ele++;
4260 // Mark the element as repeated
4261 repeated = true;
4262 break;
4263 }
4264 }
4265
4266 // Create new face element
4267 if (!repeated)
4268 {
4269 // Add the pair of nodes (edge) to the node dones
4270 done_nodes_pt.push_back(tmp_pair);
4271 // Add the element to the face elements
4272 face_el_pt.push_back(tmp_ele_pt);
4273 }
4274 else
4275 {
4276 // Free the repeated bulk element!!
4277 delete tmp_ele_pt;
4278 tmp_ele_pt = 0;
4279 }
4280
4281 // Re-start
4282 repeated = false;
4283
4284 } // for (e < nel)
4285 } // if (nel > 0)
4286
4287 } // else (n_regions > 1)
4288
4289 // Do not consider the repeated elements
4290 nele -= n_repeated_ele;
4291
4292#ifdef PARANOID
4293 if (nele != face_el_pt.size())
4294 {
4295 std::ostringstream error_message;
4296 error_message
4297 << "The independet counting of face elements (" << nele << ") for "
4298 << "boundary (" << b << ") is different\n"
4299 << "from the real number of face elements in the container ("
4300 << face_el_pt.size() << ")\n";
4301 //<< "Possible memory leak\n"
4302 throw OomphLibError(
4303 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4304 }
4305#endif
4306
4307 // ----------------------------------------------------------------
4308 // Second: Sort the face elements, only consider nonhalo elements
4309 // ----------------------------------------------------------------
4310
4311 // Get the total number of nonhalo face elements
4312 const unsigned nnon_halo_face_elements = face_el_pt.size();
4313
4314 // The vector of list to store the "segments" that compound the
4315 // boundary (segments may appear only in a distributed mesh)
4316 Vector<std::list<FiniteElement*>> segment_sorted_ele_pt;
4317
4318 // Number of already sorted face elements
4319 unsigned nsorted_face_elements = 0;
4320
4321 // Keep track of who's done
4322 std::map<FiniteElement*, bool> done_el;
4323
4324 // Keep track of which element is inverted
4325 std::map<FiniteElement*, bool> is_inverted;
4326
4327 // Iterate until all possible segments have been created
4328 while (nsorted_face_elements < nnon_halo_face_elements)
4329 {
4330 // The ordered list of face elements (in a distributed mesh a
4331 // collection of contiguous face elements define a segment)
4332 std::list<FiniteElement*> sorted_el_pt;
4333
4334#ifdef PARANOID
4335 // Select an initial element for the segment
4336 bool found_initial_face_element = false;
4337#endif
4338
4339 FiniteElement* ele_face_pt = 0;
4340
4341 unsigned iface = 0;
4342 for (iface = 0; iface < nele; iface++)
4343 {
4344 ele_face_pt = face_el_pt[iface];
4345 // If not done then take it as initial face element
4346 if (!done_el[ele_face_pt])
4347 {
4348#ifdef PARANOID
4349 // Mark as found the root face element
4350 found_initial_face_element = true;
4351#endif
4352 // Increase the number of sorted face elements
4353 nsorted_face_elements++;
4354 // Increase the counter to mark the position of the next
4355 // element number
4356 iface++;
4357 // Add the face element in the list of sorted face elements
4358 sorted_el_pt.push_back(ele_face_pt);
4359 // Mark as done
4360 done_el[ele_face_pt] = true;
4361 break;
4362 } // if (!done_el[ele_face_pt])
4363 } // for (iface < nele)
4364
4365#ifdef PARANOID
4366 if (!found_initial_face_element)
4367 {
4368 std::ostringstream error_message;
4369 error_message
4370 << "Could not find an initial face element for the current segment\n";
4371 throw OomphLibError(
4372 error_message.str(),
4373 "TriangleMesh::re_assign_initial_zeta_values_for_internal_boundary()",
4374 OOMPH_EXCEPTION_LOCATION);
4375 }
4376#endif
4377
4378 // Number of nodes
4379 const unsigned nnod = ele_face_pt->nnode();
4380
4381 // Left and rightmost nodes (the left and right nodes of the
4382 // current face element)
4383 Node* left_node_pt = ele_face_pt->node_pt(0);
4384 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
4385
4386 // Continue iterating if a new face element has been added to the
4387 // list
4388 bool face_element_added = false;
4389
4390 // While a new face element has been added to the set of sorted
4391 // face elements then re-iterate
4392 do
4393 {
4394 // Start from the next face element since we have already added
4395 // the previous one as the initial face element (any previous
4396 // face element had to be added on previous iterations)
4397 for (unsigned iiface = iface; iiface < nele; iiface++)
4398 {
4399 // Re-start flag
4400 face_element_added = false;
4401
4402 // Get the candidate element
4403 ele_face_pt = face_el_pt[iiface];
4404
4405 // Check that the candidate element has not been done and is
4406 // not a halo element
4407 if (!(done_el[ele_face_pt]))
4408 {
4409 // Get the left and right nodes of the current element
4410 Node* local_left_node_pt = ele_face_pt->node_pt(0);
4411 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
4412
4413 // New element fits at the left of segment and is not inverted
4414 if (left_node_pt == local_right_node_pt)
4415 {
4416 left_node_pt = local_left_node_pt;
4417 sorted_el_pt.push_front(ele_face_pt);
4418 is_inverted[ele_face_pt] = false;
4419 face_element_added = true;
4420 }
4421 // New element fits at the left of segment and is inverted
4422 else if (left_node_pt == local_left_node_pt)
4423 {
4424 left_node_pt = local_right_node_pt;
4425 sorted_el_pt.push_front(ele_face_pt);
4426 is_inverted[ele_face_pt] = true;
4427 face_element_added = true;
4428 }
4429 // New element fits on the right of segment and is not inverted
4430 else if (right_node_pt == local_left_node_pt)
4431 {
4432 right_node_pt = local_right_node_pt;
4433 sorted_el_pt.push_back(ele_face_pt);
4434 is_inverted[ele_face_pt] = false;
4435 face_element_added = true;
4436 }
4437 // New element fits on the right of segment and is inverted
4438 else if (right_node_pt == local_right_node_pt)
4439 {
4440 right_node_pt = local_left_node_pt;
4441 sorted_el_pt.push_back(ele_face_pt);
4442 is_inverted[ele_face_pt] = true;
4443 face_element_added = true;
4444 }
4445
4446 if (face_element_added)
4447 {
4448 done_el[ele_face_pt] = true;
4449 nsorted_face_elements++;
4450 break;
4451 } // if (face_element_added)
4452
4453 } // if (!(done_el[ele_face_pt]))
4454
4455 } // for (iiface<nnon_halo_face_element)
4456
4457 } while (face_element_added &&
4458 (nsorted_face_elements < nnon_halo_face_elements));
4459
4460 // Store the created segment in the vector of segments
4461 segment_sorted_ele_pt.push_back(sorted_el_pt);
4462
4463 } // while(nsorted_face_elements < nnon_halo_face_elements);
4464
4465 // --------------------------------------------------------------
4466 // Third: We have the face elements sorted, now assign boundary
4467 // coordinates to the nodes in the segments and compute the
4468 // arclength of the segment.
4469 // --------------------------------------------------------------
4470
4471 // The number of segments in this processor
4472 const unsigned nsegments = segment_sorted_ele_pt.size();
4473
4474#ifdef PARANOID
4475 if (nnon_halo_face_elements > 0 && nsegments == 0)
4476 {
4477 std::ostringstream error_message;
4478 error_message
4479 << "The number of segments is zero, but the number of nonhalo\n"
4480 << "elements is: (" << nnon_halo_face_elements << ")\n";
4481 throw OomphLibError(
4482 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4483 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
4484#endif
4485
4486 // Vector of sets that stores the nodes of each segment based on a
4487 // lexicographically order starting from the bottom left node of
4488 // each segment
4489 Vector<std::set<Node*>> segment_all_nodes_pt(nsegments);
4490
4491 // Stores the nodes on each segment in the order they appear in the
4492 // face elements
4493 Vector<Vector<Node*>> sorted_segment_all_nodes_pt(nsegments);
4494
4495 // Associate and arclength to each node on each segment of the
4496 // boundary, the nodes and therefore the arclength come in the same
4497 // order as the face elements
4498 Vector<Vector<double>> sorted_segment_node_arclength(nsegments);
4499
4500 // The arclength of each segment in the current processor
4501 Vector<double> segment_arclength(nsegments);
4502
4503 // The number of vertices of each segment
4504 Vector<unsigned> nvertices_per_segment(nsegments);
4505
4506 // The initial zeta for the segment
4507 Vector<double> initial_zeta_segment(nsegments);
4508
4509 // The final zeta for the segment
4510 Vector<double> final_zeta_segment(nsegments);
4511
4512 // Go through all the segments and compute the LOCAL boundary
4513 // coordinates
4514 for (unsigned is = 0; is < nsegments; is++)
4515 {
4516#ifdef PARANOID
4517 if (segment_sorted_ele_pt[is].size() == 0)
4518 {
4519 std::ostringstream error_message;
4520 error_message << "The (" << is << ")-th segment has no elements\n";
4521 throw OomphLibError(
4522 error_message.str(),
4523 "TriangleMesh::re_assign_initial_zeta_values_for_internal_boundary()",
4524 OOMPH_EXCEPTION_LOCATION);
4525 } // if (segment_sorted_ele_pt[is].size() == 0)
4526#endif
4527
4528 // Get access to the first element on the segment
4529 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
4530
4531 // Number of nodes
4532 const unsigned nnod = first_ele_pt->nnode();
4533
4534 // Get the first node of the current segment
4535 Node* first_node_pt = first_ele_pt->node_pt(0);
4536 if (is_inverted[first_ele_pt])
4537 {
4538 first_node_pt = first_ele_pt->node_pt(nnod - 1);
4539 }
4540
4541 // Coordinates of left node
4542 double x_left = first_node_pt->x(0);
4543 double y_left = first_node_pt->x(1);
4544
4545 // Initialise boundary coordinate (local boundary coordinate for
4546 // boundaries with more than one segment)
4547 Vector<double> zeta(1, 0.0);
4548
4549 // If we have associated a GeomObject then it is not necessary
4550 // to compute the arclength, only read the values from the nodes at
4551 // the edges
4552 if (this->boundary_geom_object_pt(b) != 0)
4553 {
4554 first_node_pt->get_coordinates_on_boundary(b, zeta);
4555 initial_zeta_segment[is] = zeta[0];
4556
4557 // Get access to the last element on the segment
4558 FiniteElement* last_ele_pt = segment_sorted_ele_pt[is].back();
4559
4560 // Get the last node of the current segment
4561 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
4562 if (is_inverted[last_ele_pt])
4563 {
4564 last_node_pt = last_ele_pt->node_pt(0);
4565 }
4566
4567 last_node_pt->get_coordinates_on_boundary(b, zeta);
4568 final_zeta_segment[is] = zeta[0];
4569 }
4570
4571 // Sort the nodes in the segment (lexicographically bottom left
4572 // node)
4573 std::set<Node*> local_nodes_pt;
4574 local_nodes_pt.insert(first_node_pt);
4575
4576 // Associate and arclength to the sorted nodes
4577 Vector<double> sorted_node_arclength;
4578 sorted_node_arclength.push_back(0.0);
4579
4580 // Sorts the nodes in the segments according their sorting in the
4581 // face elements
4582 Vector<Node*> sorted_nodes_pt;
4583 sorted_nodes_pt.push_back(first_node_pt);
4584
4585 // Now loop over nodes in order
4586 for (std::list<FiniteElement*>::iterator it =
4587 segment_sorted_ele_pt[is].begin();
4588 it != segment_sorted_ele_pt[is].end();
4589 it++)
4590 {
4591 // Get the face element
4592 FiniteElement* el_pt = *it;
4593
4594 // Start node and increment
4595 unsigned k_nod = 1;
4596 int nod_diff = 1;
4597 if (is_inverted[el_pt])
4598 {
4599 k_nod = nnod - 2;
4600 nod_diff = -1;
4601 }
4602
4603 // Loop over nodes
4604 for (unsigned j = 1; j < nnod; j++)
4605 {
4606 Node* nod_pt = el_pt->node_pt(k_nod);
4607 k_nod += nod_diff;
4608
4609 // Coordinates of right node
4610 double x_right = nod_pt->x(0);
4611 double y_right = nod_pt->x(1);
4612
4613 // Increment boundary coordinate
4614 zeta[0] += sqrt((x_right - x_left) * (x_right - x_left) +
4615 (y_right - y_left) * (y_right - y_left));
4616
4617 // When we have a GeomObject associated to the boundary we already
4618 // know the zeta values for the nodes, there is no need to compute
4619 // the arclength
4620 if (this->boundary_geom_object_pt(b) == 0)
4621 {
4622 // Set boundary coordinate
4623 // nod_pt->set_coordinates_on_boundary(b, zeta);
4624 }
4625
4626 // Increment reference coordinate
4627 x_left = x_right;
4628 y_left = y_right;
4629
4630 // Get lexicographically bottom left node but only
4631 // use vertex nodes as candidates
4632 local_nodes_pt.insert(nod_pt);
4633
4634 // Associate the arclength for the current node
4635 sorted_node_arclength.push_back(zeta[0]);
4636
4637 // Store the node in the sorted nodes storage
4638 sorted_nodes_pt.push_back(nod_pt);
4639
4640 } // for (j < nnod)
4641
4642 } // iterator over the elements in the segment
4643
4644 // Info. to be passed to the other processors
4645 // The initial arclength for the segment that goes after this depends
4646 // on the current segment arclength
4647 segment_arclength[is] = zeta[0];
4648
4649 // Info. to be passed to the other processors
4650 // The initial vertex number for the segment that goes after this
4651 // depends on the current sement vertices number
4652 nvertices_per_segment[is] = local_nodes_pt.size();
4653
4654 // Add the nodes for the corresponding segment in the container
4655 segment_all_nodes_pt[is] = local_nodes_pt;
4656
4657 // Add the arclengths to the nodes in the segment
4658 sorted_segment_node_arclength[is] = sorted_node_arclength;
4659
4660 // Add the sorted nodes to the storage
4661 sorted_segment_all_nodes_pt[is] = sorted_nodes_pt;
4662
4663 // The attaching of the halo elements at both sides of the segments is
4664 // performed only if segments connectivity needs to be computed
4665
4666 } // for (is < nsegments)
4667
4668 // ------------------------------------------------------------------
4669 // Fourth: Now we have the segments sorted, with arclength and with
4670 // LOCAL boundary coordinates assigned to the nodes. Identify the
4671 // nodes on the segments with the input segments and re-assign all
4672 // the info. related with the identification of segments
4673 // ------------------------------------------------------------------
4674
4675 // Get the number of segments for the old sorted segments
4676 const unsigned old_nsegments = old_segment_sorted_ele_pt.size();
4677
4678 // ------------------------------------------------------------------
4679 // Copy the old info. in temporary storages
4680 Vector<unsigned> old_boundary_segment_inverted(old_nsegments);
4681
4682 Vector<Vector<double>> old_boundary_segment_initial_coordinate(
4683 old_nsegments);
4684 Vector<Vector<double>> old_boundary_segment_final_coordinate(old_nsegments);
4685
4686 Vector<double> old_boundary_segment_initial_zeta(old_nsegments);
4687 Vector<double> old_boundary_segment_final_zeta(old_nsegments);
4688
4689 Vector<double> old_boundary_segment_initial_arclength(old_nsegments);
4690 Vector<double> old_boundary_segment_final_arclength(old_nsegments);
4691
4692 // Back-up the information
4693 for (unsigned old_is = 0; old_is < old_nsegments; old_is++)
4694 {
4695 old_boundary_segment_inverted[old_is] =
4696 boundary_segment_inverted(b)[old_is];
4697
4698 old_boundary_segment_initial_coordinate[old_is].resize(2);
4699 old_boundary_segment_final_coordinate[old_is].resize(2);
4700 for (unsigned i = 0; i < 2; i++)
4701 {
4702 old_boundary_segment_initial_coordinate[old_is][i] =
4703 boundary_segment_initial_coordinate(b)[old_is][i];
4704
4705 old_boundary_segment_final_coordinate[old_is][i] =
4706 boundary_segment_final_coordinate(b)[old_is][i];
4707 }
4708
4709 // Check if the boundary has an associated GeomObject
4710 if (this->boundary_geom_object_pt(b) != 0)
4711 {
4712 old_boundary_segment_initial_zeta[old_is] =
4713 boundary_segment_initial_zeta(b)[old_is];
4714
4715 old_boundary_segment_final_zeta[old_is] =
4716 boundary_segment_final_zeta(b)[old_is];
4717
4718 } // if (this->boundary_geom_object_pt(b)!=0)
4719 else
4720 {
4721 old_boundary_segment_initial_arclength[old_is] =
4722 boundary_segment_initial_arclength(b)[old_is];
4723
4724 old_boundary_segment_final_arclength[old_is] =
4725 boundary_segment_final_arclength(b)[old_is];
4726
4727 } // else if (this->boundary_geom_object_pt(b)!=0)
4728
4729 } // for (old_is < old_nsegments)
4730
4731 // ------------------------------------------------------------------
4732 // Now clear the original storages
4733 Boundary_segment_inverted[b].clear();
4734 Boundary_segment_initial_coordinate[b].clear();
4735 Boundary_segment_final_coordinate[b].clear();
4736
4737 Boundary_segment_initial_zeta[b].clear();
4738 Boundary_segment_final_zeta[b].clear();
4739
4740 Boundary_segment_initial_arclength[b].clear();
4741 Boundary_segment_final_arclength[b].clear();
4742 // ------------------------------------------------------------------
4743 // .. and resize the storages for the new number of segments
4744 Boundary_segment_inverted[b].resize(nsegments);
4745 Boundary_segment_initial_coordinate[b].resize(nsegments);
4746 Boundary_segment_final_coordinate[b].resize(nsegments);
4747
4748 // Check if the boundary has an associated GeomObject
4749 if (this->boundary_geom_object_pt(b) != 0)
4750 {
4751 Boundary_segment_initial_zeta[b].resize(nsegments);
4752 Boundary_segment_final_zeta[b].resize(nsegments);
4753 }
4754 else
4755 {
4756 Boundary_segment_initial_arclength[b].resize(nsegments);
4757 Boundary_segment_final_arclength[b].resize(nsegments);
4758 }
4759 // ------------------------------------------------------------------
4760 // map to know if the new segment has been re-assigned the info.
4761 std::map<unsigned, bool> done_segment;
4762
4763 // Count the number of re-assigned segments with the new values
4764 unsigned re_assigned_segments = 0;
4765
4766 // Go through all the old segments (the input segments)
4767 for (unsigned old_is = 0; old_is < old_nsegments; old_is++)
4768 {
4769 // Get the first and last zeta values for the current segment
4770 const double old_initial_arclength =
4771 old_boundary_segment_initial_arclength[old_is];
4772 const double old_final_arclength =
4773 old_boundary_segment_final_arclength[old_is];
4774 // Get the "is inverted" segment information
4775 const unsigned old_inverted_segment =
4776 old_boundary_segment_inverted[old_is];
4777
4778 // Check if the boundary coordinates in the segment go in
4779 // increasing or decreasing order
4780 bool old_increasing_order = false;
4781 if (old_initial_arclength < old_final_arclength)
4782 {
4783 old_increasing_order = true;
4784 }
4785
4786 // Now get the first and last node of the current segment
4787 // Get the first element
4788 FiniteElement* first_old_seg_ele_pt =
4789 old_segment_sorted_ele_pt[old_is].front();
4790
4791 // Number of nodes
4792 const unsigned nnod = first_old_seg_ele_pt->nnode();
4793
4794 // Get the first node of the current segment
4795 Node* first_old_seg_node_pt = first_old_seg_ele_pt->node_pt(0);
4796 if (old_is_inverted[first_old_seg_ele_pt])
4797 {
4798 first_old_seg_node_pt = first_old_seg_ele_pt->node_pt(nnod - 1);
4799 }
4800
4801 // Get access to the last element on the segment
4802 FiniteElement* last_old_seg_ele_pt =
4803 old_segment_sorted_ele_pt[old_is].back();
4804
4805 // Get the last node of the current segment
4806 Node* last_old_seg_node_pt = last_old_seg_ele_pt->node_pt(nnod - 1);
4807 if (old_is_inverted[last_old_seg_ele_pt])
4808 {
4809 last_old_seg_node_pt = last_old_seg_ele_pt->node_pt(0);
4810 }
4811 // Check if the segment is inverted, if that is the case then
4812 // also invert the nodes
4813 if (old_inverted_segment)
4814 {
4815 Node* temp_node_pt = first_old_seg_node_pt;
4816 first_old_seg_node_pt = last_old_seg_node_pt;
4817 last_old_seg_node_pt = temp_node_pt;
4818 }
4819
4820 // We have the first and last node of the old segment (input
4821 // segment), now identify in which segment, of those with only
4822 // nonhalo face elements, they are
4823 for (unsigned is = 0; is < nsegments; is++)
4824 {
4825 if (!done_segment[is])
4826 {
4827 // Go through the nodes of the current segment and try to find
4828 // the old nodes
4829 bool found_first_old_seg_node = false;
4830 bool found_last_old_seg_node = false;
4831 bool same_order = false;
4832
4833 // Get the first node of the current segment
4834 FiniteElement* first_seg_ele_pt = segment_sorted_ele_pt[is].front();
4835 Node* first_seg_node_pt = first_seg_ele_pt->node_pt(0);
4836 if (is_inverted[first_seg_ele_pt])
4837 {
4838 first_seg_node_pt = first_seg_ele_pt->node_pt(nnod - 1);
4839 }
4840
4841 // Get the arclength for the first node
4842 const double segment_first_node_zeta =
4843 sorted_segment_node_arclength[is][0];
4844
4845 // Get the node coordinates for the first node
4846 Vector<double> first_node_coord(2);
4847 for (unsigned i = 0; i < 2; i++)
4848 {
4849 first_node_coord[i] = first_seg_node_pt->x(i);
4850 }
4851
4852 // Get the last node of the current segment
4853 FiniteElement* last_seg_ele_pt = segment_sorted_ele_pt[is].back();
4854 Node* last_seg_node_pt = last_seg_ele_pt->node_pt(nnod - 1);
4855 if (is_inverted[last_seg_ele_pt])
4856 {
4857 last_seg_node_pt = last_seg_ele_pt->node_pt(0);
4858 }
4859
4860 // Get the arclength for the last node
4861 const double segment_final_node_zeta = segment_arclength[is];
4862
4863 // Get the node coordinates for the last node
4864 Vector<double> last_node_coord(2);
4865 for (unsigned i = 0; i < 2; i++)
4866 {
4867 last_node_coord[i] = last_seg_node_pt->x(i);
4868 }
4869
4870 // Temporary storage for the nodes of the current segment
4871 Vector<Node*> segment_node_pt = sorted_segment_all_nodes_pt[is];
4872 // Get the number of nodes in the segment
4873 const unsigned nsegment_node = segment_node_pt.size();
4874 for (unsigned in = 0; in < nsegment_node; in++)
4875 {
4876 Node* current_node_pt = segment_node_pt[in];
4877 if (!found_first_old_seg_node &&
4878 first_old_seg_node_pt == current_node_pt)
4879 {
4880 // Get the arclength assigned to the node on the old
4881 // segment
4882 const double current_node_zeta =
4883 sorted_segment_node_arclength[is][in];
4884
4885 // Now check if the new segment has the same orientation
4886 // as the old one
4887 if (!found_last_old_seg_node) // has the same orientation
4888 {
4889 // Re-assign the first node coordinates
4890 Boundary_segment_initial_coordinate[b][is] = first_node_coord;
4891
4892 // Check if the boundary has an associated GeomObject
4893 if (this->boundary_geom_object_pt(b) != 0)
4894 {
4895 // Assign the zeta values if the current segment has the
4896 // nodes of the old one
4897
4898 // If we are in the same order then pass the values as
4899 // they are
4900 Boundary_segment_initial_zeta[b][is] =
4901 initial_zeta_segment[is];
4902
4903 } // if (this->boundary_geom_object_pt(b)!=0)
4904 else
4905 {
4906 // Get the distance to the first node
4907 const double distance =
4908 std::fabs(current_node_zeta - segment_first_node_zeta);
4909
4910 double new_initial_arclength = old_initial_arclength;
4911
4912 // Now check if the zeta values are in increasing order
4913 if (old_increasing_order)
4914 {
4915 // Substract the distance
4916 new_initial_arclength -= distance;
4917 }
4918 else
4919 {
4920 // Add the distance
4921 new_initial_arclength += distance;
4922 }
4923
4924 // Re-assign the initial arclength for the current segment
4925 Boundary_segment_initial_arclength[b][is] =
4926 new_initial_arclength;
4927
4928 } // else if (this->boundary_geom_object_pt(b)!=0)
4929 } // if (!found_last_old_seg_node)
4930 else // has different orientation
4931 {
4932 // Re-assign the first node coordinates
4933 Boundary_segment_initial_coordinate[b][is] = last_node_coord;
4934
4935 // Check if the boundary has an associated GeomObject
4936 if (this->boundary_geom_object_pt(b) != 0)
4937 {
4938 // Assign the zeta values if the current segment has the
4939 // nodes of the old one
4940
4941 // Not the same order, we need to copy the zeta values
4942 // from the other end, the inverted flag is changed at
4943 // the end. Copy the value from the final end
4944 Boundary_segment_initial_zeta[b][is] = final_zeta_segment[is];
4945
4946 } // if (this->boundary_geom_object_pt(b)!=0)
4947 else
4948 {
4949 // Get the distance to the final node
4950 const double distance =
4951 std::fabs(current_node_zeta - segment_final_node_zeta);
4952
4953 double new_initial_arclength = old_initial_arclength;
4954
4955 // Now check if the zeta values are in increasing order
4956 if (old_increasing_order)
4957 {
4958 // Substract the distance
4959 new_initial_arclength -= distance;
4960 }
4961 else
4962 {
4963 // Add the distance
4964 new_initial_arclength += distance;
4965 }
4966
4967 // Re-assign the initial arclength for the current segment
4968 Boundary_segment_initial_arclength[b][is] =
4969 new_initial_arclength;
4970
4971 } // else if (this->boundary_geom_object_pt(b)!=0)
4972 } // else if (!found_last_old_seg_node)
4973
4974 // Mark as found the first node
4975 found_first_old_seg_node = true;
4976 }
4977 // if (!found_first_old_seg_node &&
4978 // first_old_seg_node_pt == current_node_pt)
4979
4980 // If we found first the first node then the segments have
4981 // the same order
4982 if (found_first_old_seg_node && !found_last_old_seg_node)
4983 {
4984 same_order = true;
4985 }
4986
4987 if (!found_last_old_seg_node &&
4988 last_old_seg_node_pt == current_node_pt)
4989 {
4990 // Get the boundary coordinates assigned to the node on
4991 // the old segment
4992 const double current_node_zeta =
4993 sorted_segment_node_arclength[is][in];
4994
4995 // Now check if the new segment has the same orientation
4996 // as the old one
4997 if (found_first_old_seg_node) // has the same orientation
4998 {
4999 // Re-assign the last node coordinates
5000 Boundary_segment_final_coordinate[b][is] = last_node_coord;
5001
5002 // Check if the boundary has an associated GeomObject
5003 if (this->boundary_geom_object_pt(b) != 0)
5004 {
5005 // Assign the zeta values if the current segment has the
5006 // nodes of the old one
5007
5008 // If we are in the same order then pass the values as
5009 // they are
5010 Boundary_segment_final_zeta[b][is] = final_zeta_segment[is];
5011
5012 } // if (this->boundary_geom_object_pt(b)!=0)
5013 else
5014 {
5015 // Get the distance to the last node
5016 const double distance =
5017 std::fabs(current_node_zeta - segment_final_node_zeta);
5018
5019 double new_final_arclength = old_final_arclength;
5020
5021 // Now check if the zeta values are in increasing order
5022 if (old_increasing_order)
5023 {
5024 // Add the distance
5025 new_final_arclength += distance;
5026 }
5027 else
5028 {
5029 // Substract the distance
5030 new_final_arclength -= distance;
5031 }
5032
5033 // Re-assign the final arclength for the current segment
5034 Boundary_segment_final_arclength[b][is] = new_final_arclength;
5035
5036 } // else if (this->boundary_geom_object_pt(b)!=0)
5037 } // if (found_first_old_seg_node)
5038 else
5039 {
5040 // Re-assign the last node coordinates
5041 Boundary_segment_final_coordinate[b][is] = first_node_coord;
5042
5043 // Check if the boundary has an associated GeomObject
5044 if (this->boundary_geom_object_pt(b) != 0)
5045 {
5046 // Assign the zeta values if the current segment has the
5047 // nodes of the old one
5048
5049 // Not the same order, we need to copy the zeta values
5050 // from the other end, the inverted flag is changed at
5051 // the end. Copy the value from the initial end
5052 Boundary_segment_final_zeta[b][is] = initial_zeta_segment[is];
5053
5054 } // if (this->boundary_geom_object_pt(b)!=0)
5055 else
5056 {
5057 // Get the distance to the last node
5058 const double distance =
5059 std::fabs(current_node_zeta - segment_first_node_zeta);
5060
5061 double new_final_arclength = old_final_arclength;
5062
5063 // Now check if the zeta values are in increasing order
5064 if (old_increasing_order)
5065 {
5066 // Add the distance
5067 new_final_arclength += distance;
5068 }
5069 else
5070 {
5071 // Substract the distance
5072 new_final_arclength -= distance;
5073 }
5074
5075 // Re-assign the final arclength for the current segment
5076 Boundary_segment_final_arclength[b][is] = new_final_arclength;
5077
5078 } // else if (this->boundary_geom_object_pt(b)!=0)
5079 } // if (found_first_old_seg_node)
5080
5081 // Mark as found the last node
5082 found_last_old_seg_node = true;
5083
5084 } // if (!found_last_old_seg_node &&
5085 // last_old_seg_node_pt == current_node_pt)
5086
5087 // If we found the last node first then the segments have
5088 // not the same order
5089 if (!found_first_old_seg_node && found_last_old_seg_node)
5090 {
5091 same_order = false;
5092 }
5093
5094 if (found_first_old_seg_node && found_last_old_seg_node)
5095 {
5096 // Check if necessary to change the information that
5097 // states if a segment is inverted or not
5098 if (same_order)
5099 {
5100 Boundary_segment_inverted[b][is] = old_inverted_segment;
5101 }
5102 else
5103 {
5104 Boundary_segment_inverted[b][is] = !old_inverted_segment;
5105 }
5106
5107 // Mark the segment as done
5108 done_segment[is] = true;
5109
5110 // Increase the number of re-assigned segments
5111 re_assigned_segments++;
5112
5113 // Break the for that look for the nodes in the segments
5114 break;
5115 }
5116
5117 } // for (in < nsegment_node)
5118
5119#ifdef PARANOID
5120 if ((found_first_old_seg_node && !found_last_old_seg_node) ||
5121 (!found_first_old_seg_node && found_last_old_seg_node))
5122 {
5123 std::stringstream error_message;
5124 error_message
5125 << "Working with boundary (" << b << ").\nOnly the first node or "
5126 << "the last node of the old segment (" << old_is << ") was\n"
5127 << "found. Both, first and last node should have been found in "
5128 << "the same segment!!!.\n"
5129 << "Found first seg node:" << found_first_old_seg_node << "\n"
5130 << "Found last seg node:" << found_last_old_seg_node << "\n\n";
5131 throw OomphLibError(error_message.str(),
5132 "TriangleMesh::re_assign_initial_zeta_values_"
5133 "for_internal_boundary()",
5134 OOMPH_EXCEPTION_LOCATION);
5135 }
5136#endif
5137
5138 } // if (!done_segment[is])
5139 } // for (is < nsegments)
5140 } // for (old_is < old_nsegments)
5141
5142 // For those segments not identified set dummy values, the boundary
5143 // coordinates should be corrected at the synchronisation stage
5144
5145 // loop over the new segments and check if there not identified
5146 // segments
5147 for (unsigned is = 0; is < nsegments; is++)
5148 {
5149 // Was the segment identified
5150 if (!done_segment[is])
5151 {
5152 // Get the first node of the current segment
5153 FiniteElement* first_seg_ele_pt = segment_sorted_ele_pt[is].front();
5154 // Number of nodes
5155 const unsigned nnod = first_seg_ele_pt->nnode();
5156
5157 Node* first_seg_node_pt = first_seg_ele_pt->node_pt(0);
5158 if (is_inverted[first_seg_ele_pt])
5159 {
5160 first_seg_node_pt = first_seg_ele_pt->node_pt(nnod - 1);
5161 }
5162
5163 // Get the arclength for the first node
5164 const double segment_first_node_zeta =
5165 sorted_segment_node_arclength[is][0];
5166
5167 // Get the node coordinates for the first node
5168 Vector<double> first_node_coord(2);
5169 for (unsigned i = 0; i < 2; i++)
5170 {
5171 first_node_coord[i] = first_seg_node_pt->x(i);
5172 }
5173
5174 // Get the last node of the current segment
5175 FiniteElement* last_seg_ele_pt = segment_sorted_ele_pt[is].back();
5176 Node* last_seg_node_pt = last_seg_ele_pt->node_pt(nnod - 1);
5177 if (is_inverted[last_seg_ele_pt])
5178 {
5179 last_seg_node_pt = last_seg_ele_pt->node_pt(0);
5180 }
5181
5182 // Get the arclength for the last node
5183 const double segment_final_node_zeta = segment_arclength[is];
5184
5185 // Get the node coordinates for the last node
5186 Vector<double> last_node_coord(2);
5187 for (unsigned i = 0; i < 2; i++)
5188 {
5189 last_node_coord[i] = last_seg_node_pt->x(i);
5190 }
5191
5192 // Re-assign the initial node coordinates
5193 Boundary_segment_initial_coordinate[b][is] = first_node_coord;
5194
5195 // Check if the boundary has an associated GeomObject
5196 if (this->boundary_geom_object_pt(b) != 0)
5197 {
5198 // Assign the zeta values if the current segment has the
5199 // nodes of the old one
5200
5201 // If we are in the same order then pass the values as
5202 // they are
5203 Boundary_segment_initial_zeta[b][is] = initial_zeta_segment[is];
5204
5205 } // if (this->boundary_geom_object_pt(b)!=0)
5206 else
5207 {
5208 // Re-assign the initial arclength for the current segment
5209 Boundary_segment_initial_arclength[b][is] = segment_first_node_zeta;
5210
5211 } // else if (this->boundary_geom_object_pt(b)!=0)
5212
5213 // Re-assign the initial node coordinates
5214 Boundary_segment_final_coordinate[b][is] = last_node_coord;
5215
5216 // Check if the boundary has an associated GeomObject
5217 if (this->boundary_geom_object_pt(b) != 0)
5218 {
5219 // Assign the zeta values if the current segment has the
5220 // nodes of the old one
5221
5222 // If we are in the same order then pass the values as
5223 // they are
5224 Boundary_segment_final_zeta[b][is] = final_zeta_segment[is];
5225
5226 } // if (this->boundary_geom_object_pt(b)!=0)
5227 else
5228 {
5229 // Re-assign the final arclength for the current segment
5230 Boundary_segment_final_arclength[b][is] = segment_final_node_zeta;
5231
5232 } // else if (this->boundary_geom_object_pt(b)!=0)
5233
5234 Boundary_segment_inverted[b][is] = 0;
5235
5236 // Mark the segment as done
5237 done_segment[is] = true;
5238
5239 // Increase the number of re-assigned segments
5240 re_assigned_segments++;
5241
5242 } // if (!done_segment[is])
5243
5244 } // for (is < nsegments)
5245
5246#ifdef PARANOID
5247 // Compare the number of new segments identified with the old segments
5248 if (re_assigned_segments != nsegments)
5249 {
5250 std::stringstream error_message;
5251 error_message << "Working with boundary (" << b
5252 << ").\nThe number of re-assigned "
5253 << "segments (" << re_assigned_segments
5254 << ") is different from the number\nof segments ("
5255 << nsegments << ")\n\n";
5256 throw OomphLibError(
5257 error_message.str(),
5258 "TriangleMesh::re_assign_initial_zeta_values_for_internal_boundary()",
5259 OOMPH_EXCEPTION_LOCATION);
5260 } // if (re_assigned_segments != nsegments)
5261#endif
5262
5263 // Clean all the created face elements
5264 for (unsigned i = 0; i < nele; i++)
5265 {
5266 delete face_el_pt[i];
5267 face_el_pt[i] = 0;
5268 }
5269 }
5270
5271 /// =====================================================================
5272 /// Select face elements from a given boundary. In case the we are
5273 /// dealing with an internal boundary we use a set of criterias to
5274 /// decide which of the two face elements should be used on represent
5275 /// the internal boundary. We return the face elements, halo or
5276 /// haloed on this processor that form the boundary. The caller method
5277 /// should be in charge of selecting nonhalo elements and deleting the face
5278 /// elements created by this method
5279 /// =====================================================================
5280 template<class ELEMENT>
5282 Vector<FiniteElement*>& face_ele_pt,
5283 const unsigned& b,
5284 bool& is_internal_boundary,
5285 std::map<FiniteElement*, FiniteElement*>& face_to_bulk_element_pt)
5286 {
5287 // Get the communicator of the mesh
5288 OomphCommunicator* comm_pt = this->communicator_pt();
5289
5290 const unsigned my_rank = comm_pt->my_rank();
5291
5292 // ------------------------------------------------------------------
5293 // 1) Get the face elements associated with the current boundary
5294 // ------------------------------------------------------------------
5295
5296 // Temporary storage for face elements (do not take care of
5297 // repeated face elements)
5298 Vector<FiniteElement*> tmp_face_ele_pt;
5299
5300 const unsigned nregions = this->nregion();
5301
5302 // If there is more than one region then only use boundary
5303 // coordinates from the bulk side (region 0)
5304 if (nregions > 1)
5305 {
5306 for (unsigned ir = 0; ir < nregions; ir++)
5307 {
5308 const unsigned region_id =
5309 static_cast<unsigned>(this->Region_attribute[ir]);
5310
5311 // Loop over all elements on boundaries in region -ir-
5312 const unsigned nele_in_region =
5313 this->nboundary_element_in_region(b, region_id);
5314
5315 // Only bother to do anything else, if there are elements
5316 // associated with the boundary and the current region
5317 if (nele_in_region > 0)
5318 {
5319 // Loop over the bulk elements adjacent to boundary b
5320 for (unsigned e = 0; e < nele_in_region; e++)
5321 {
5322 // Get pointer to the bulk element that is adjacent
5323 // to boundary b
5324 FiniteElement* bulk_ele_pt =
5325 this->boundary_element_in_region_pt(b, region_id, e);
5326
5327 // Get the index of the face of element e along
5328 // boundary b
5329 int face_index =
5330 this->face_index_at_boundary_in_region(b, region_id, e);
5331
5332 // Create the face element
5333 FiniteElement* tmp_face_el_pt =
5334 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
5335
5336 // Associated the face element with the bulk
5337 face_to_bulk_element_pt[tmp_face_el_pt] = bulk_ele_pt;
5338
5339 // ... and add it to the tmp storage for all the
5340 // face elements, do not take care for repeated
5341 // ones (at the moment)
5342 tmp_face_ele_pt.push_back(tmp_face_el_pt);
5343
5344 } // for (e < nele_in_region)
5345
5346 } // if (nele_in_region > 0)
5347
5348 } // for (ir < n_regions)
5349
5350 } // if (n_regions > 1)
5351
5352 // Otherwise it's just the normal boundary functions
5353 else
5354 {
5355 // Loop over all elements on boundaries
5356 const unsigned nbound_ele = this->nboundary_element(b);
5357
5358 // Only bother to do anything else, if there are elements
5359 if (nbound_ele > 0)
5360 {
5361 // Loop over the bulk elements adjacent to boundary b
5362 for (unsigned e = 0; e < nbound_ele; e++)
5363 {
5364 // Get pointer to the bulk element that is adjacent to
5365 // boundary b
5366 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
5367
5368 // Get the index of the face of element e along
5369 // boundary b
5370 int face_index = this->face_index_at_boundary(b, e);
5371
5372 // Create the face element
5373 FiniteElement* tmp_face_el_pt =
5374 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
5375
5376 // Associated the face element with the bulk
5377 face_to_bulk_element_pt[tmp_face_el_pt] = bulk_ele_pt;
5378
5379 // ... and add it to the tmp storage for all the face
5380 // elements, do not care for repeated ones (at the
5381 // moment)
5382 tmp_face_ele_pt.push_back(tmp_face_el_pt);
5383
5384 } // (e < nbound_ele)
5385
5386 } // (nbound_ele > 0)
5387
5388 } // else (n_regions > 1)
5389
5390 // map to know which face element has been already done
5391 std::map<FiniteElement*, bool> done_face;
5392
5393 // Set the flag to indicate if we are working with an internal
5394 // boundary
5395 is_internal_boundary = false;
5396
5397 // Free the memory of the elements in this container (only used
5398 // when working with internal boundaries)
5399 Vector<FiniteElement*> free_memory_face_ele_pt;
5400
5401 // Get the number of face elements in the boundary (including
5402 // repeated)
5403 const unsigned n_tmp_face_ele = tmp_face_ele_pt.size();
5404 for (unsigned ie = 0; ie < n_tmp_face_ele; ie++)
5405 {
5406 // Get the possible main element
5407 FiniteElement* main_face_ele_pt = tmp_face_ele_pt[ie];
5408 if (!done_face[main_face_ele_pt])
5409 {
5410 // Mark the face element as done
5411 done_face[main_face_ele_pt] = true;
5412 // Get the number of nodes for the face element
5413 const unsigned nnodes = main_face_ele_pt->nnode();
5414 // Get the first and last node of the main face element
5415 Node* main_first_node_pt = main_face_ele_pt->node_pt(0);
5416 Node* main_last_node_pt = main_face_ele_pt->node_pt(nnodes - 1);
5417 // Look for the other side face element (we can start from
5418 // the next one, all previous face elements have been
5419 // already identified with its other side face)
5420 for (unsigned iie = ie + 1; iie < n_tmp_face_ele; iie++)
5421 {
5422 // Get the possible dependant element
5423 FiniteElement* dependant_face_ele_pt = tmp_face_ele_pt[iie];
5424 if (!done_face[dependant_face_ele_pt])
5425 {
5426 // Get the first and last node of the dependant
5427 // face element
5428 Node* dependant_first_node_pt = dependant_face_ele_pt->node_pt(0);
5429 Node* dependant_last_node_pt =
5430 dependant_face_ele_pt->node_pt(nnodes - 1);
5431 // Check if the nodes at the ends of both face
5432 // elements match (also check the reversed case)
5433 if (((dependant_first_node_pt == main_first_node_pt) &&
5434 (dependant_last_node_pt == main_last_node_pt)) ||
5435 ((dependant_first_node_pt == main_last_node_pt) &&
5436 (dependant_last_node_pt == main_first_node_pt)))
5437 {
5438 // Set the flag to indicate we are working with an
5439 // internal boundary
5440 is_internal_boundary = true;
5441 // Mark the face element as done
5442 done_face[dependant_face_ele_pt] = true;
5443
5444 // Now choose which face element will be used
5445 // as the main element. We get the processor in
5446 // charge of the element and choose the one
5447 // with the highest processor in charge or the
5448 // bottom-left bulk element in case the both
5449 // faces are on the same processor
5450
5451 // Get the bulk element for each face element
5452 // (the main and the dependant face element)
5453 FiniteElement* main_bulk_ele_pt =
5454 face_to_bulk_element_pt[main_face_ele_pt];
5455 FiniteElement* dependant_bulk_ele_pt =
5456 face_to_bulk_element_pt[dependant_face_ele_pt];
5457
5458 // Get the processor in charge for each bulk
5459 // element
5460 int processor_in_charge_main_bulk_ele =
5461 main_bulk_ele_pt->non_halo_proc_ID();
5462 int processor_in_charge_dependant_bulk_ele =
5463 dependant_bulk_ele_pt->non_halo_proc_ID();
5464
5465 // If the processor in charge is negative the
5466 // element is not halo, therefore the processor
5467 // in charge is the current one
5468 if (processor_in_charge_main_bulk_ele < 0)
5469 {
5470 processor_in_charge_main_bulk_ele = static_cast<int>(my_rank);
5471 }
5472 if (processor_in_charge_dependant_bulk_ele < 0)
5473 {
5474 processor_in_charge_dependant_bulk_ele =
5475 static_cast<int>(my_rank);
5476 }
5477
5478 // Flag to know if add the main or dependant
5479 // face element
5480 bool add_main_face_element = true;
5481 if (processor_in_charge_dependant_bulk_ele >
5482 processor_in_charge_main_bulk_ele)
5483 {
5484 // Include the dependant element
5485 add_main_face_element = false;
5486 }
5487 else if (processor_in_charge_main_bulk_ele ==
5488 processor_in_charge_dependant_bulk_ele)
5489 {
5490 // When the processor in charge for both
5491 // elements is the same then use the
5492 // bottom-left criteria on the bulk
5493 // elements to choose the main face element
5494 Vector<double> main_ele_coordinates(2);
5495 Vector<double> dependant_ele_coordinates(2);
5496 // Get the number of nodes on the bulk
5497 // elements
5498 const unsigned n_bulk_nodes = main_bulk_ele_pt->nnode();
5499 for (unsigned inode = 0; inode < n_bulk_nodes; inode++)
5500 {
5501 for (unsigned idim = 0; idim < 2; idim++)
5502 {
5503 main_ele_coordinates[idim] +=
5504 main_bulk_ele_pt->node_pt(inode)->x(idim);
5505 dependant_ele_coordinates[idim] +=
5506 dependant_bulk_ele_pt->node_pt(inode)->x(idim);
5507 } // (idim < 2)
5508
5509 } // (inode < n_bulk_nodes)
5510
5511 // Get the average of the nodes coordinates
5512 for (unsigned idim = 0; idim < 2; idim++)
5513 {
5514 main_ele_coordinates[idim] /= (double)n_bulk_nodes;
5515 dependant_ele_coordinates[idim] /= (double)n_bulk_nodes;
5516 }
5517
5518 // Once we know the average coordinates for
5519 // each element then we choose the one with
5520 // the bottom-left averaged coordinates
5521 if (dependant_ele_coordinates[1] < main_ele_coordinates[1])
5522 {
5523 add_main_face_element = false;
5524 }
5525 else if (dependant_ele_coordinates[1] ==
5526 main_ele_coordinates[1])
5527 {
5528 // The left-most element
5529 if (dependant_ele_coordinates[0] < main_ele_coordinates[0])
5530 {
5531 add_main_face_element = false;
5532 }
5533 }
5534 } // else -- The processor in charge is the
5535 // same for both elements
5536
5537 if (add_main_face_element)
5538 {
5539 // Add the main face element to the storage
5540 // so we get the halo and haloed nodes from
5541 // it
5542 face_ele_pt.push_back(main_face_ele_pt);
5543 // Mark the dependat face element to free
5544 // its memory
5545 free_memory_face_ele_pt.push_back(dependant_face_ele_pt);
5546 }
5547 else
5548 {
5549 // Add the dependant face element to the
5550 // storage so we get the halo and haloed
5551 // nodes from it
5552 face_ele_pt.push_back(dependant_face_ele_pt);
5553 // Mark the main face element to free its
5554 // memory
5555 free_memory_face_ele_pt.push_back(main_face_ele_pt);
5556 }
5557
5558 // Break the for to look for the next face
5559 // element
5560 break;
5561
5562 } // if -- matching of nodes from main ele and
5563 // dependant ele
5564
5565 } // if (!done_face[dependant_face_ele_pt])
5566
5567 } // for (iie < n_tmp_face_ele)
5568
5569 } // if (!done_face[main_face_ele_pt])
5570
5571 } // for (ie < n_tmp_face_ele)
5572
5573 // Are there any face element to free its memory
5574 const unsigned n_free_face_ele = free_memory_face_ele_pt.size();
5575 if (n_free_face_ele == 0)
5576 {
5577 // If there is not face elements to free memory that means that
5578 // we are not working with an internal boundary, therefore copy
5579 // all the element from the tmp face elements into the face
5580 // elements container
5581
5582 // Resize the container
5583 face_ele_pt.resize(n_tmp_face_ele);
5584 // loop over the elements and copy them
5585 for (unsigned i = 0; i < n_tmp_face_ele; i++)
5586 {
5587 face_ele_pt[i] = tmp_face_ele_pt[i];
5588 } // for (i < n_tmp_face_ele)
5589
5590 } // if (n_free_face_ele == 0)
5591 else
5592 {
5593 // ... otherwise free the memory of the indicated elements
5594 // loop over the elements to free its memory
5595 for (unsigned i = 0; i < n_free_face_ele; i++)
5596 {
5597 delete free_memory_face_ele_pt[i];
5598 free_memory_face_ele_pt[i] = 0;
5599 } // for (i < n_free_face_ele)
5600 }
5601 }
5602
5603 /// ========================================================================
5604 /// In charge of sinchronize the boundary coordinates for internal
5605 /// boundaries that were split as part of the distribution
5606 /// process. Called after setup_boundary_coordinates() for the
5607 /// original mesh only
5608 /// ========================================================================
5609 template<class ELEMENT>
5611 const unsigned& b)
5612 {
5613 // ------------------------------------------------------------------
5614 // First: Get the face elements associated with the current boundary
5615 // ------------------------------------------------------------------
5616
5617 // Get the communicator of the mesh
5618 OomphCommunicator* comm_pt = this->communicator_pt();
5619
5620 const unsigned nproc = comm_pt->nproc();
5621 const unsigned my_rank = comm_pt->my_rank();
5622
5623 // Temporary storage for face elements (do not take care of repeated
5624 // face elements)
5625 Vector<FiniteElement*> tmp_face_ele_pt;
5626
5627 const unsigned nregions = this->nregion();
5628
5629 // map to associate the face element to the bulk element, necessary
5630 // to get the processor in charge for the halo elements
5631 std::map<FiniteElement*, FiniteElement*> face_to_bulk_element_pt;
5632
5633 // If there is more than one region then only use boundary
5634 // coordinates from the bulk side (region 0)
5635 if (nregions > 1)
5636 {
5637 for (unsigned ir = 0; ir < nregions; ir++)
5638 {
5639 const unsigned region_id =
5640 static_cast<unsigned>(this->Region_attribute[ir]);
5641
5642 // Loop over all elements on boundaries in region -ir-
5643 const unsigned nele_in_region =
5644 this->nboundary_element_in_region(b, region_id);
5645
5646 // Only bother to do anything else, if there are elements
5647 // associated with the boundary and the current region
5648 if (nele_in_region > 0)
5649 {
5650 // Loop over the bulk elements adjacent to boundary b
5651 for (unsigned e = 0; e < nele_in_region; e++)
5652 {
5653 // Get pointer to the bulk element that is adjacent to boundary b
5654 FiniteElement* bulk_ele_pt =
5655 this->boundary_element_in_region_pt(b, region_id, e);
5656
5657 // Get the index of the face of element e along boundary b
5658 int face_index =
5659 this->face_index_at_boundary_in_region(b, region_id, e);
5660
5661 // Create the face element
5662 FiniteElement* tmp_face_el_pt =
5663 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
5664
5665 // ... and add it to the tmp storage for all the face
5666 // elements, do not take care for repeated ones (at the
5667 // moment)
5668 tmp_face_ele_pt.push_back(tmp_face_el_pt);
5669 // Create the map to know if the element is halo
5670 face_to_bulk_element_pt[tmp_face_el_pt] = bulk_ele_pt;
5671
5672 } // for (e < nele_in_region)
5673
5674 } // if (nele_in_region > 0)
5675
5676 } // for (ir < n_regions)
5677
5678 } // if (n_regions > 1)
5679
5680 // Otherwise it's just the normal boundary functions
5681 else
5682 {
5683 // Loop over all elements on boundaries
5684 const unsigned nbound_ele = this->nboundary_element(b);
5685
5686 // Only bother to do anything else, if there are elements
5687 if (nbound_ele > 0)
5688 {
5689 // Loop over the bulk elements adjacent to boundary b
5690 for (unsigned e = 0; e < nbound_ele; e++)
5691 {
5692 // Get pointer to the bulk element that is adjacent to boundary b
5693 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
5694
5695 // Get the index of the face of element e along boundary b
5696 int face_index = this->face_index_at_boundary(b, e);
5697
5698 // Create the face element
5699 FiniteElement* tmp_face_el_pt =
5700 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
5701
5702 // ... and add it to the tmp storage for all the face
5703 // elements, do not care for repeated ones (at the moment)
5704 tmp_face_ele_pt.push_back(tmp_face_el_pt);
5705 // Create the map to know if the element is halo
5706 face_to_bulk_element_pt[tmp_face_el_pt] = bulk_ele_pt;
5707
5708 } // (e < nbound_ele)
5709
5710 } // (nbound_ele > 0)
5711
5712 } // else (n_regions > 1)
5713
5714 // Temporary storage for one side face elements. In case we are
5715 // working with an internal boundary here we store only one of the
5716 // face elements that are at each side of the boundary
5717 Vector<FiniteElement*> face_ele_pt;
5718
5719 // map to know which face element has been already done
5720 std::map<FiniteElement*, bool> done_face;
5721
5722 // Flag to indicate if we are working with an internal boundary
5723 bool is_internal_boundary = false;
5724
5725#ifdef PARANOID
5726 // Flag to indicate if we are working with an internal boundary (paranoid)
5727 bool is_internal_boundary_paranoid = false;
5728
5729 // Count the number of other side face elements found in case we are
5730 // working with an internal boundary
5731 unsigned nfound_face_elements = 0;
5732#endif
5733
5734 // Get the number of face elements in the boundary
5735 const unsigned nbound_ele = tmp_face_ele_pt.size();
5736 for (unsigned ie = 0; ie < nbound_ele; ie++)
5737 {
5738 // Get the possible main element
5739 FiniteElement* main_face_ele_pt = tmp_face_ele_pt[ie];
5740 if (!done_face[main_face_ele_pt])
5741 {
5742 // Mark the face element as done
5743 done_face[main_face_ele_pt] = true;
5744 // Get the number of nodes for the face element
5745 const unsigned nnodes = main_face_ele_pt->nnode();
5746 // Get the first and last node of the main face element
5747 Node* main_first_node_pt = main_face_ele_pt->node_pt(0);
5748 Node* main_last_node_pt = main_face_ele_pt->node_pt(nnodes - 1);
5749 // Look for the other side face element
5750 for (unsigned iie = ie + 1; iie < nbound_ele; iie++)
5751 {
5752 // Get the possible dependant element
5753 FiniteElement* dependant_face_ele_pt = tmp_face_ele_pt[iie];
5754 if (!done_face[dependant_face_ele_pt])
5755 {
5756 // Get the first and last node of the dependant face element
5757 Node* dependant_first_node_pt = dependant_face_ele_pt->node_pt(0);
5758 Node* dependant_last_node_pt =
5759 dependant_face_ele_pt->node_pt(nnodes - 1);
5760 // Check if the nodes at the ends of both face elements
5761 // match (also check the reversed case)
5762 if (((dependant_first_node_pt == main_first_node_pt) &&
5763 (dependant_last_node_pt == main_last_node_pt)) ||
5764 ((dependant_first_node_pt == main_last_node_pt) &&
5765 (dependant_last_node_pt == main_first_node_pt)))
5766 {
5767#ifdef PARANOID
5768 // Increase the number of found face elements
5769 nfound_face_elements += 2;
5770#endif
5771 // Set the flag to indicate we are working with an
5772 // internal boundary
5773 is_internal_boundary = true;
5774 // Mark the face element as done
5775 done_face[dependant_face_ele_pt] = true;
5776
5777 // Now choose which face element will be used as the main
5778 // element. Use the same criteria as the compute segments
5779 // connectivity method (highest processor in charge or
5780 // bottom-left bulk element)
5781
5782 // Get the bulk element for each face element (the main
5783 // and the dependant face element)
5784 FiniteElement* main_bulk_ele_pt =
5785 face_to_bulk_element_pt[main_face_ele_pt];
5786 FiniteElement* dependant_bulk_ele_pt =
5787 face_to_bulk_element_pt[dependant_face_ele_pt];
5788
5789 // Get the processor in charge for each bulk element
5790 int processor_in_charge_main_bulk_ele =
5791 main_bulk_ele_pt->non_halo_proc_ID();
5792 int processor_in_charge_dependant_bulk_ele =
5793 dependant_bulk_ele_pt->non_halo_proc_ID();
5794
5795 // If the processor in charge is negative the element is
5796 // not halo, therefore the processor in charge is the
5797 // current one
5798 if (processor_in_charge_main_bulk_ele < 0)
5799 {
5800 processor_in_charge_main_bulk_ele = static_cast<int>(my_rank);
5801 }
5802 if (processor_in_charge_dependant_bulk_ele < 0)
5803 {
5804 processor_in_charge_dependant_bulk_ele =
5805 static_cast<int>(my_rank);
5806 }
5807
5808 // Flag to know if add the main or dependant face element
5809 bool add_main_face_element = true;
5810 if (processor_in_charge_dependant_bulk_ele >
5811 processor_in_charge_main_bulk_ele)
5812 {
5813 // Include the dependant element
5814 add_main_face_element = false;
5815 }
5816 else if (processor_in_charge_main_bulk_ele ==
5817 processor_in_charge_dependant_bulk_ele)
5818 {
5819 // When the processor in charge for both elements is the same
5820 // then use the bottom-left criteria on the bulk elements to
5821 // choose the main face element
5822 Vector<double> main_ele_coordinates(2);
5823 Vector<double> dependant_ele_coordinates(2);
5824 // Get the number of nodes on the bulk elements
5825 const unsigned n_bulk_nodes = main_bulk_ele_pt->nnode();
5826 for (unsigned inode = 0; inode < n_bulk_nodes; inode++)
5827 {
5828 for (unsigned idim = 0; idim < 2; idim++)
5829 {
5830 main_ele_coordinates[idim] +=
5831 main_bulk_ele_pt->node_pt(inode)->x(idim);
5832 dependant_ele_coordinates[idim] +=
5833 dependant_bulk_ele_pt->node_pt(inode)->x(idim);
5834 } // (idim < 2)
5835 } // (inode < n_bulk_nodes)
5836
5837 // Get the average of the nodes coordinates
5838 for (unsigned idim = 0; idim < 2; idim++)
5839 {
5840 main_ele_coordinates[idim] /= (double)n_bulk_nodes;
5841 dependant_ele_coordinates[idim] /= (double)n_bulk_nodes;
5842 }
5843
5844 // Once we know the average coordinates for each element
5845 // then we choose the one with the bottom-left averaged
5846 // coordinates
5847 if (dependant_ele_coordinates[1] < main_ele_coordinates[1])
5848 {
5849 add_main_face_element = false;
5850 }
5851 else if (dependant_ele_coordinates[1] ==
5852 main_ele_coordinates[1])
5853 {
5854 // The left-most element
5855 if (dependant_ele_coordinates[0] < main_ele_coordinates[0])
5856 {
5857 add_main_face_element = false;
5858 }
5859 }
5860 } // else -- The processor in charge is the same for both
5861 // elements
5862
5863 if (add_main_face_element)
5864 {
5865 // Add the main face element to the storage so we get
5866 // the halo and haloed nodes from these face element
5867 face_ele_pt.push_back(main_face_ele_pt);
5868 }
5869 else
5870 {
5871 // Add the main face element to the storage so we get
5872 // the halo and haloed nodes from these face element
5873 face_ele_pt.push_back(dependant_face_ele_pt);
5874 }
5875
5876 // Break the for to look for the next face element
5877 break;
5878
5879 } // if -- matching of nodes from main ele and dependant ele
5880 } // if (!done_face[dependant_face_ele_pt])
5881 } // for (iie < nbound_ele)
5882 } // if (!done_face[main_face_ele_pt])
5883 } // for (ie < nbound_ele)
5884
5885 // Get the number of face elements
5886 const unsigned nface_ele = face_ele_pt.size();
5887
5888#ifdef PARANOID
5889 // Check if we are working with an internal open curve. First check
5890 // if there are elements, in a distributed approach they may be no
5891 // elements associated to the boundary
5892 if (nbound_ele > 0 && nfound_face_elements == nbound_ele)
5893 {
5894 is_internal_boundary_paranoid = true;
5895 }
5896
5897 if (nbound_ele > 0 && is_internal_boundary_paranoid &&
5898 nbound_ele != nface_ele * 2)
5899 {
5900 std::ostringstream error_message;
5901 error_message
5902 << "The info. to perform the synchronisation of the boundary "
5903 << "coordinates was not completely established\n"
5904 << "In this case it was the number of non repeated boundary elements\n"
5905 << "Number of boundary elements: (" << nbound_ele << ")\n"
5906 << "Number of nonrepeated boundary elements: (" << nface_ele << ")\n";
5907 throw OomphLibError(error_message.str(),
5908 "TriangleMesh::synchronize_boundary_coordinates()",
5909 OOMPH_EXCEPTION_LOCATION);
5910 }
5911#endif
5912
5913 // ----------------------------------------------------------------
5914 // Second: Identify the halo face elements
5915 // ----------------------------------------------------------------
5916
5917 // A flag vector to mark those face elements that are considered as
5918 // halo in the current processor
5919 std::vector<bool> is_halo_face_element(nface_ele, false);
5920
5921 // Count the total number of non halo face elements
5922 unsigned nnon_halo_face_elements = 0;
5923
5924 for (unsigned ie = 0; ie < nface_ele; ie++)
5925 {
5926 FiniteElement* face_el_pt = face_ele_pt[ie];
5927 // Get the bulk element
5928 FiniteElement* tmp_bulk_ele_pt = face_to_bulk_element_pt[face_el_pt];
5929 // Check if the bulk element is halo
5930 if (!tmp_bulk_ele_pt->is_halo())
5931 {
5932 is_halo_face_element[ie] = false;
5933 nnon_halo_face_elements++;
5934 }
5935 else
5936 {
5937 // Mark the face element as halo
5938 is_halo_face_element[ie] = true;
5939 }
5940 } // for (ie < nface_ele)
5941
5942 // -----------------------------------------------------------------
5943 // Third: Go through the face elements and get the nodes from the
5944 // elements. The boundary coordinate from each node is sent to its
5945 // processor in charge, then that processor will be responsible to
5946 // send the bound coordinate to all the processors that have a halo
5947 // representation of the node
5948 // -----------------------------------------------------------------
5949
5950 // A map to know which nodes are already done
5951 std::map<Node*, bool> done_node;
5952
5953 // The storage for the halo nodes on face elements in this processor
5954 // with other processors
5955 Vector<Vector<Node*>> face_halo_node_pt(nproc);
5956
5957 // The storage for the ids of the halo nodes on face elements in
5958 // this processor with other processors
5959 Vector<Vector<unsigned>> face_halo_node_id(nproc);
5960
5961 // The storage for the haloed nodes on face elements in this
5962 // processor with other processors
5963 Vector<Vector<Node*>> face_haloed_node_pt(nproc);
5964
5965 // The storage for the ids of the haloed nodes on face elements in
5966 // this processor with other processors
5967 Vector<Vector<unsigned>> face_haloed_node_id(nproc);
5968
5969 // A map to know which nodes are face nodes and the processor in
5970 // charge is the current one
5971 std::map<Node*, bool> done_haloed_face_node;
5972
5973 // Go through all the face elements
5974 for (unsigned iface = 0; iface < nface_ele; iface++)
5975 {
5976 // Only work with the non halo face elements
5977 if (!is_halo_face_element[iface])
5978 {
5979 // Get the face element
5980 FiniteElement* ele_face_pt = face_ele_pt[iface];
5981 // The number of nodes of the face elements
5982 const unsigned nnodes = ele_face_pt->nnode();
5983 // Go through all the nodes in the face element
5984 for (unsigned in = 0; in < nnodes; in++)
5985 {
5986 Node* face_node_pt = ele_face_pt->node_pt(in);
5987 // Check if node is done
5988 if (!done_node[face_node_pt])
5989 {
5990 // Mark the node as done
5991 done_node[face_node_pt] = true;
5992 // First check if the node is halo
5993 if (face_node_pt->is_halo())
5994 {
5995 // Get the processor in charge for the current node
5996 int int_nonhalo_ID = face_node_pt->non_halo_proc_ID();
5997#ifdef PARANOID
5998 if (int_nonhalo_ID < 0)
5999 {
6000 std::ostringstream error_message;
6001 error_message
6002 << "The node was marked to be halo but the processor in "
6003 << "charge was found to be -1\n\n";
6004 throw OomphLibError(
6005 error_message.str(),
6006 "TriangleMesh::synchronize_boundary_coordinates()",
6007 OOMPH_EXCEPTION_LOCATION);
6008 }
6009#endif
6010 const unsigned ip = static_cast<unsigned>(int_nonhalo_ID);
6011 // Add the node to the structure that holds the halo
6012 // nodes, the current processor will need to send the
6013 // info. to the processor in charge.
6014 face_halo_node_pt[ip].push_back(face_node_pt);
6015 // ... finally look for the halo id with the processor in
6016 // charge
6017#ifdef PARANOID
6018 bool found_halo_node = false;
6019#endif
6020 const unsigned nhalo_iproc = this->nhalo_node(ip);
6021 for (unsigned ihn = 0; ihn < nhalo_iproc; ihn++)
6022 {
6023 Node* compare_face_node_pt = this->halo_node_pt(ip, ihn);
6024 if (compare_face_node_pt == face_node_pt)
6025 {
6026 // Once found the id of the node with the processor
6027 // store the id in the proper storage
6028 face_halo_node_id[ip].push_back(ihn);
6029#ifdef PARANOID
6030 // Set the flag to mark as found the halo node
6031 found_halo_node = true;
6032#endif
6033 // Break the loop
6034 break;
6035 }
6036 } // for (ih < nhalo_iproc)
6037#ifdef PARANOID
6038 if (!found_halo_node)
6039 {
6040 std::ostringstream error_message;
6041 error_message
6042 << "The halo id of the current node: (" << face_node_pt->x(0)
6043 << ", " << face_node_pt->x(1) << ") with processor (" << ip
6044 << ") was not found!!!\n\n";
6045 throw OomphLibError(
6046 error_message.str(),
6047 "TriangleMesh::synchronize_boundary_coordinates()",
6048 OOMPH_EXCEPTION_LOCATION);
6049 }
6050#endif
6051 } // if (face_node_pt->is_halo())
6052 // If the node is not halo then it could be haloed. If that
6053 // is the case then store the processors at which the node
6054 // is haloed and its id. The info. of these nodes will be
6055 // sent to all the processors with a halo counterpart
6056 else
6057 {
6058 for (unsigned ip = 0; ip < nproc; ip++)
6059 {
6060 // Only work with processors different that the current one
6061 if (ip != my_rank)
6062 {
6063 // If the node is found to be haloed with the "ip"
6064 // processor then save the haloed id in the storage.
6065 // The current processor needs to send info. to the
6066 // other processors to establish the boundary
6067 // coordinates
6068
6069 // Get the number of haloed nodes with processor ip
6070 const unsigned nhaloed_iproc = this->nhaloed_node(ip);
6071 for (unsigned ihdn = 0; ihdn < nhaloed_iproc; ihdn++)
6072 {
6073 Node* compare_face_node_pt = this->haloed_node_pt(ip, ihdn);
6074 if (face_node_pt == compare_face_node_pt)
6075 {
6076 // Store the node on the haloed node vector for
6077 // the corresponding processor
6078 face_haloed_node_pt[ip].push_back(face_node_pt);
6079 // Now store the halo id of the node with the
6080 // current processor
6081 face_haloed_node_id[ip].push_back(ihdn);
6082 // Mark the node as haloed with other processors,
6083 // so we know the processor in charge is the
6084 // current one "my_rank".
6085 done_haloed_face_node[face_node_pt] = true;
6086 // Break looking in the current processor, look in
6087 // the next one
6088 break;
6089 } // if (face_node_pt == compare_face_node_pt)
6090 } // for (ihdn < nhaloed_node_iproc)
6091 } // if (ip != my_rank)
6092 } // for (ip < nproc)
6093 } // else (non halo node)
6094 } // if (!done_node[node_face_pt])
6095 } // for (in < nnodes)
6096 } // if (!is_halo_face_element[iface])
6097 } // for (iface < nface_ele)
6098
6099 // -----------------------------------------------------------------
6100 // Fourth: Go through the halo nodes, package and send the
6101 // info. necessary to identify the face nodes in the processor in
6102 // charge. Identify the haloed nodes in the processor in charge and
6103 // establish the boundary coordinates, check if those nodes are
6104 // (already) marked as faced nodes, if that is the case then do not
6105 // establish the boundary coordinates but register them to send back
6106 // the info. to all the processors that have a halo representation
6107 // of the face node
6108 // -----------------------------------------------------------------
6109
6110 // Go through all processors
6111 for (unsigned ip = 0; ip < nproc; ip++)
6112 {
6113 // Only work with processors different than the current one
6114 if (ip != my_rank)
6115 {
6116 const unsigned nhalo_face_nodes = face_halo_node_pt[ip].size();
6117#ifdef PARANOID
6118 if (nhalo_face_nodes != face_halo_node_id[ip].size())
6119 {
6120 std::ostringstream error_message;
6121 error_message
6122 << "The number of found halo face nodes (" << nhalo_face_nodes
6123 << ") is different from the number of\nfound halo face ids ("
6124 << face_halo_node_id[ip].size() << ")!!!\n\n";
6125 throw OomphLibError(
6126 error_message.str(),
6127 "TriangleMesh::synchronize_boundary_coordinates()",
6128 OOMPH_EXCEPTION_LOCATION);
6129 }
6130#endif
6131
6132 // Container to send the info. related with the halo nodes to be
6133 // identified in the processors in charge
6134 Vector<unsigned> flat_unsigned_send_packed_data;
6135 Vector<double> flat_double_send_packed_data;
6136
6137 // Go through the halo face nodes in the "ip" processor
6138 for (unsigned ihfn = 0; ihfn < nhalo_face_nodes; ihfn++)
6139 {
6140 // Get the "ihfn"-th face node with the "ip" processor
6141 Node* halo_face_node_pt = face_halo_node_pt[ip][ihfn];
6142 // Get the halo id with the "ip" processor
6143 const unsigned halo_id = face_halo_node_id[ip][ihfn];
6144 // Get the boundary coordinate of the node
6145 Vector<double> zeta(1);
6146 halo_face_node_pt->get_coordinates_on_boundary(b, zeta);
6147 // Store the info. in the containers
6148 flat_unsigned_send_packed_data.push_back(halo_id);
6149 flat_double_send_packed_data.push_back(zeta[0]);
6150 }
6151
6152 // Send the info.
6153 MPI_Status status;
6154 MPI_Request request;
6155
6156 // Processor to which send the info
6157 int send_proc = static_cast<int>(ip);
6158 // Processor from which receive the info
6159 int receive_proc = static_cast<int>(ip);
6160
6161 // Storage to receive the info.
6162 Vector<unsigned> flat_unsigned_receive_packed_data;
6163 Vector<double> flat_double_receive_packed_data;
6164
6165 // --------------
6166 // Unsigned data
6167 unsigned nflat_unsigned_send = flat_unsigned_send_packed_data.size();
6168 MPI_Isend(&nflat_unsigned_send,
6169 1,
6170 MPI_UNSIGNED,
6171 send_proc,
6172 1,
6173 comm_pt->mpi_comm(),
6174 &request);
6175
6176 unsigned nflat_unsigned_receive = 0;
6177 MPI_Recv(&nflat_unsigned_receive,
6178 1,
6179 MPI_UNSIGNED,
6180 receive_proc,
6181 1,
6182 comm_pt->mpi_comm(),
6183 &status);
6184
6185 MPI_Wait(&request, MPI_STATUS_IGNORE);
6186
6187 if (nflat_unsigned_send != 0)
6188 {
6189 MPI_Isend(&flat_unsigned_send_packed_data[0],
6190 nflat_unsigned_send,
6191 MPI_UNSIGNED,
6192 send_proc,
6193 2,
6194 comm_pt->mpi_comm(),
6195 &request);
6196 }
6197
6198 if (nflat_unsigned_receive != 0)
6199 {
6200 flat_unsigned_receive_packed_data.resize(nflat_unsigned_receive);
6201 MPI_Recv(&flat_unsigned_receive_packed_data[0],
6202 nflat_unsigned_receive,
6203 MPI_UNSIGNED,
6204 receive_proc,
6205 2,
6206 comm_pt->mpi_comm(),
6207 &status);
6208 }
6209
6210 if (nflat_unsigned_send != 0)
6211 {
6212 MPI_Wait(&request, MPI_STATUS_IGNORE);
6213 }
6214
6215 // --------------
6216 // Double data
6217 unsigned nflat_double_send = flat_double_send_packed_data.size();
6218 MPI_Isend(&nflat_double_send,
6219 1,
6220 MPI_DOUBLE,
6221 send_proc,
6222 3,
6223 comm_pt->mpi_comm(),
6224 &request);
6225
6226 unsigned nflat_double_receive = 0;
6227 MPI_Recv(&nflat_double_receive,
6228 1,
6229 MPI_DOUBLE,
6230 receive_proc,
6231 3,
6232 comm_pt->mpi_comm(),
6233 &status);
6234
6235 MPI_Wait(&request, MPI_STATUS_IGNORE);
6236
6237 if (nflat_double_send != 0)
6238 {
6239 MPI_Isend(&flat_double_send_packed_data[0],
6240 nflat_double_send,
6241 MPI_DOUBLE,
6242 send_proc,
6243 4,
6244 comm_pt->mpi_comm(),
6245 &request);
6246 }
6247
6248 if (nflat_double_receive != 0)
6249 {
6250 flat_double_receive_packed_data.resize(nflat_double_receive);
6251 MPI_Recv(&flat_double_receive_packed_data[0],
6252 nflat_double_receive,
6253 MPI_DOUBLE,
6254 receive_proc,
6255 4,
6256 comm_pt->mpi_comm(),
6257 &status);
6258 }
6259
6260 if (nflat_double_send != 0)
6261 {
6262 MPI_Wait(&request, MPI_STATUS_IGNORE);
6263 }
6264 // --------------
6265
6266#ifdef PARANOID
6267 if (nflat_unsigned_receive != nflat_double_receive)
6268 {
6269 std::ostringstream error_message;
6270 error_message << "The number of unsigned received data ("
6271 << nflat_unsigned_receive << ") is different from the "
6272 << "number\nof double received data ("
6273 << nflat_double_receive << ")!!!\n\n";
6274 throw OomphLibError(
6275 error_message.str(),
6276 "TriangleMesh::synchronize_boundary_coordinates()",
6277 OOMPH_EXCEPTION_LOCATION);
6278 }
6279#endif
6280
6281 // With the received info. establish the boundary coordinates
6282 // for the face nodes that this processor is in charge (haloed
6283 // nodes)
6284 for (unsigned iflat_packed = 0; iflat_packed < nflat_unsigned_receive;
6285 iflat_packed++)
6286 {
6287 // Get the haloed id for the node
6288 const unsigned haloed_id =
6289 flat_unsigned_receive_packed_data[iflat_packed];
6290 // Get the boundary coordinates
6291 Vector<double> zeta(1);
6292 zeta[0] = flat_double_receive_packed_data[iflat_packed];
6293
6294 // Get the haloed node
6295 Node* haloed_face_node_pt = this->haloed_node_pt(ip, haloed_id);
6296
6297 // If the node has already set the boundary coordinates then
6298 // do not establish it. This is the case for the nodes that
6299 // lie on the boundary, for those nodes not identified on the
6300 // boundary since no elements lie on the boundary but the node
6301 // is on the boundary (a corner of an element lies on the
6302 // boundary) set boundary coordinates and register them to
6303 // send their info. to the processors with a halo counterpart
6304
6305 // If the node is not haloed face in the procesor in charge
6306 // then set the boundary coordinates and register the node to
6307 // send back the boundary coordinates to the processors with a
6308 // halo counterpart
6309 if (!done_haloed_face_node[haloed_face_node_pt])
6310 {
6311 // Establish the boundary coordinates
6312 haloed_face_node_pt->set_coordinates_on_boundary(b, zeta);
6313
6314 // Look in all processors where the node could be halo
6315 for (unsigned iiproc = 0; iiproc < nproc; iiproc++)
6316 {
6317 // Only work with processors different than the current one
6318 if (iiproc != my_rank)
6319 {
6320 // Get the number of haloed nodes with processor iiproc
6321 const unsigned nhaloed_node_iiproc = this->nhaloed_node(iiproc);
6322 for (unsigned ihdn = 0; ihdn < nhaloed_node_iiproc; ihdn++)
6323 {
6324 Node* compare_haloed_node_pt =
6325 this->haloed_node_pt(iiproc, ihdn);
6326 if (haloed_face_node_pt == compare_haloed_node_pt)
6327 {
6328 // Store the node on the haloed node vector for the
6329 // corresponding processor
6330 face_haloed_node_pt[iiproc].push_back(haloed_face_node_pt);
6331 // Now store the halo id of the node with the current
6332 // processor
6333 face_haloed_node_id[iiproc].push_back(ihdn);
6334 // Break searching in the current processor, search in
6335 // the next one
6336 break;
6337 } // if (haloed_face_node_pt==compare_haloed_face_node_pt)
6338 } // for (ihdn < nhaloed_node_iproc)
6339 } // if (iiproc != my_rank)
6340 } // for (iiproc < nproc)
6341 } // if (!done_haloed_face_node[haloed_face_node_pt])
6342 } // for (iflat_packed < nflat_unsigned_receive)
6343 } // if (ip != my_rank)
6344 } // for (ip < nproc)
6345
6346 // -----------------------------------------------------------------
6347 // Fifth: The boundary coordinates have been established in the
6348 // processors in charge of the nodes. Now each processor send back
6349 // the boundary coordinates to all the processors where there is a
6350 // halo representation of the node
6351 // -----------------------------------------------------------------
6352
6353 // Go through all processors
6354 for (unsigned ip = 0; ip < nproc; ip++)
6355 {
6356 // Only work with processors different than the current one
6357 if (ip != my_rank)
6358 {
6359 // Container to send the info. of the haloed nodes to all the
6360 // processors
6361 Vector<unsigned> flat_unsigned_send_packed_data;
6362 Vector<double> flat_double_send_packed_data;
6363
6364 // Get the total number of haloed face nodes with the "ip"
6365 // processor
6366 const unsigned nhaloed_face_nodes = face_haloed_node_pt[ip].size();
6367 // Go through the haloed face nodes in the "ip" processor
6368 for (unsigned ihdfn = 0; ihdfn < nhaloed_face_nodes; ihdfn++)
6369 {
6370 // Get the "ihdfn"-th face node with the "ip" processor
6371 Node* haloed_face_node_pt = face_haloed_node_pt[ip][ihdfn];
6372 // Get the haloed id with the "ip" processor
6373 const unsigned haloed_id = face_haloed_node_id[ip][ihdfn];
6374 // Get the boundary coordinate of the node
6375 Vector<double> zeta(1);
6376 haloed_face_node_pt->get_coordinates_on_boundary(b, zeta);
6377 // Store the info. in the containers
6378 flat_unsigned_send_packed_data.push_back(haloed_id);
6379 flat_double_send_packed_data.push_back(zeta[0]);
6380 }
6381
6382 // Send the info.
6383 MPI_Status status;
6384 MPI_Request request;
6385
6386 // Processor to which send the info
6387 int send_proc = static_cast<int>(ip);
6388 // Processor from which receive the info
6389 int receive_proc = static_cast<int>(ip);
6390
6391 // Storage to receive the info.
6392 Vector<unsigned> flat_unsigned_receive_packed_data;
6393 Vector<double> flat_double_receive_packed_data;
6394
6395 // --------------
6396 // Unsigned data
6397 unsigned nflat_unsigned_send = flat_unsigned_send_packed_data.size();
6398 MPI_Isend(&nflat_unsigned_send,
6399 1,
6400 MPI_UNSIGNED,
6401 send_proc,
6402 1,
6403 comm_pt->mpi_comm(),
6404 &request);
6405
6406 unsigned nflat_unsigned_receive = 0;
6407 MPI_Recv(&nflat_unsigned_receive,
6408 1,
6409 MPI_UNSIGNED,
6410 receive_proc,
6411 1,
6412 comm_pt->mpi_comm(),
6413 &status);
6414
6415 MPI_Wait(&request, MPI_STATUS_IGNORE);
6416
6417 if (nflat_unsigned_send != 0)
6418 {
6419 MPI_Isend(&flat_unsigned_send_packed_data[0],
6420 nflat_unsigned_send,
6421 MPI_UNSIGNED,
6422 send_proc,
6423 2,
6424 comm_pt->mpi_comm(),
6425 &request);
6426 }
6427
6428 if (nflat_unsigned_receive != 0)
6429 {
6430 flat_unsigned_receive_packed_data.resize(nflat_unsigned_receive);
6431 MPI_Recv(&flat_unsigned_receive_packed_data[0],
6432 nflat_unsigned_receive,
6433 MPI_UNSIGNED,
6434 receive_proc,
6435 2,
6436 comm_pt->mpi_comm(),
6437 &status);
6438 }
6439
6440 if (nflat_unsigned_send != 0)
6441 {
6442 MPI_Wait(&request, MPI_STATUS_IGNORE);
6443 }
6444
6445 // --------------
6446 // Double data
6447 unsigned nflat_double_send = flat_double_send_packed_data.size();
6448 MPI_Isend(&nflat_double_send,
6449 1,
6450 MPI_DOUBLE,
6451 send_proc,
6452 3,
6453 comm_pt->mpi_comm(),
6454 &request);
6455
6456 unsigned nflat_double_receive = 0;
6457 MPI_Recv(&nflat_double_receive,
6458 1,
6459 MPI_DOUBLE,
6460 receive_proc,
6461 3,
6462 comm_pt->mpi_comm(),
6463 &status);
6464
6465 MPI_Wait(&request, MPI_STATUS_IGNORE);
6466
6467 if (nflat_double_send != 0)
6468 {
6469 MPI_Isend(&flat_double_send_packed_data[0],
6470 nflat_double_send,
6471 MPI_DOUBLE,
6472 send_proc,
6473 4,
6474 comm_pt->mpi_comm(),
6475 &request);
6476 }
6477
6478 if (nflat_double_receive != 0)
6479 {
6480 flat_double_receive_packed_data.resize(nflat_double_receive);
6481 MPI_Recv(&flat_double_receive_packed_data[0],
6482 nflat_double_receive,
6483 MPI_DOUBLE,
6484 receive_proc,
6485 4,
6486 comm_pt->mpi_comm(),
6487 &status);
6488 }
6489
6490 if (nflat_double_send != 0)
6491 {
6492 MPI_Wait(&request, MPI_STATUS_IGNORE);
6493 }
6494 // --------------
6495
6496#ifdef PARANOID
6497 if (nflat_unsigned_receive != nflat_double_receive)
6498 {
6499 std::ostringstream error_message;
6500 error_message << "The number of unsigned received data ("
6501 << nflat_unsigned_receive << ") is different from the "
6502 << "number\nof double received data ("
6503 << nflat_double_receive << ")!!!\n\n";
6504 throw OomphLibError(
6505 error_message.str(),
6506 "TriangleMesh::synchronize_boundary_coordinates()",
6507 OOMPH_EXCEPTION_LOCATION);
6508 }
6509#endif
6510
6511 // With the received info. establish the boundary coordinates
6512 // received for the face nodes that this processor is not in
6513 // charge (halo nodes)
6514 for (unsigned iflat_packed = 0; iflat_packed < nflat_unsigned_receive;
6515 iflat_packed++)
6516 {
6517 // Get the halo id for the node
6518 const unsigned halo_id =
6519 flat_unsigned_receive_packed_data[iflat_packed];
6520 // Get the boundary coordinates
6521 Vector<double> zeta(1);
6522 zeta[0] = flat_double_receive_packed_data[iflat_packed];
6523
6524 // Get the halo node
6525 Node* halo_face_node_pt = this->halo_node_pt(ip, halo_id);
6526
6527 // It could be possible that the node has been already
6528 // established boundary coordinates since it is a halo face
6529 // node. However, for those elements not on the boundary, but
6530 // having a corner node on the boundary this procedure will
6531 // establish boundary coordinates for those nodes
6532
6533 // this->add_boundary_node(b, halo_face_node_pt);
6534
6535 // Establish the boundary coordinates
6536 halo_face_node_pt->set_coordinates_on_boundary(b, zeta);
6537 } // for (iflat_packed < nflat_unsigned_receive)
6538 } // if (ip != my_rank)
6539 } // for (ip < nproc)
6540
6541 // Clean all the created face elements
6542 for (unsigned ie = 0; ie < nbound_ele; ie++)
6543 {
6544 delete tmp_face_ele_pt[ie];
6545 tmp_face_ele_pt[ie] = 0;
6546 }
6547
6548 // Now get a new face mesh representation and fill the data for those
6549 // processors with halo segments
6550 if (is_internal_boundary)
6551 {
6552 re_scale_re_assigned_initial_zeta_values_for_internal_boundary(b);
6553 }
6554 }
6555
6556 //======================================================================
6557 /// Re-assign the boundary segments initial zeta (arclength)
6558 /// for those internal boundaries that were splited during the
6559 /// distribution process (only apply for internal boundaries that
6560 /// have one face element at each side of the boundary)
6561 //======================================================================
6562 template<class ELEMENT>
6565 const unsigned& b)
6566 {
6567 // ------------------------------------------------------------------
6568 // First: Get the face elements associated with the current boundary
6569 // Only include nonhalo face elements
6570 // ------------------------------------------------------------------
6571 // Temporary storage for face elements
6572 Vector<FiniteElement*> face_el_pt;
6573
6574 // Temporary storage for the number of elements adjacent to the
6575 // boundary
6576 unsigned nele = 0;
6577
6578 // Temporary storage for elements adjacent to the boundary that have
6579 // a common edge (related with internal boundaries)
6580 unsigned n_repeated_ele = 0;
6581
6582 const unsigned n_regions = this->nregion();
6583
6584 // Temporary storage for already done nodes
6585 Vector<std::pair<Node*, Node*>> done_nodes_pt;
6586
6587 // If there is more than one region then only use boundary
6588 // coordinates from the bulk side (region 0)
6589 if (n_regions > 1)
6590 {
6591 for (unsigned rr = 0; rr < n_regions; rr++)
6592 {
6593 const unsigned region_id =
6594 static_cast<unsigned>(this->Region_attribute[rr]);
6595
6596 // Loop over all elements on boundaries in region i_r
6597 const unsigned nel_in_region =
6598 this->nboundary_element_in_region(b, region_id);
6599
6600 unsigned nel_repetead_in_region = 0;
6601
6602 // Only bother to do anything else, if there are elements
6603 // associated with the boundary and the current region
6604 if (nel_in_region > 0)
6605 {
6606 bool repeated = false;
6607
6608 // Loop over the bulk elements adjacent to boundary b
6609 for (unsigned e = 0; e < nel_in_region; e++)
6610 {
6611 // Get pointer to the bulk element that is adjacent to
6612 // boundary b
6613 FiniteElement* bulk_elem_pt =
6614 this->boundary_element_in_region_pt(b, region_id, e);
6615
6616 // Remember only to work with nonhalo elements
6617 if (bulk_elem_pt->is_halo())
6618 {
6619 n_repeated_ele++;
6620 continue;
6621 }
6622
6623 // Find the index of the face of element e along boundary b
6624 int face_index =
6625 this->face_index_at_boundary_in_region(b, region_id, e);
6626
6627 // Before adding the new element we need to be sure that the
6628 // edge that this element represent has not been already
6629 // added
6630 FiniteElement* tmp_ele_pt =
6631 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
6632
6633 const unsigned n_nodes = tmp_ele_pt->nnode();
6634
6635 std::pair<Node*, Node*> tmp_pair = std::make_pair(
6636 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
6637
6638 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
6639 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
6640
6641 // Search for repeated nodes
6642 const unsigned n_done_nodes = done_nodes_pt.size();
6643 for (unsigned l = 0; l < n_done_nodes; l++)
6644 {
6645 if (tmp_pair == done_nodes_pt[l] ||
6646 tmp_pair_inverse == done_nodes_pt[l])
6647 {
6648 nel_repetead_in_region++;
6649 repeated = true;
6650 break;
6651 }
6652 }
6653
6654 // Create new face element
6655 if (!repeated)
6656 {
6657 // Add the pair of nodes (edge) to the node dones
6658 done_nodes_pt.push_back(tmp_pair);
6659 // Add the element to the face elements
6660 face_el_pt.push_back(tmp_ele_pt);
6661 }
6662 else
6663 {
6664 // Clean up
6665 delete tmp_ele_pt;
6666 tmp_ele_pt = 0;
6667 }
6668
6669 // Re-start
6670 repeated = false;
6671
6672 } // for nel
6673
6674 nele += nel_in_region;
6675
6676 n_repeated_ele += nel_repetead_in_region;
6677
6678 } // if (nel_in_region > 0)
6679 } // for (rr < n_regions)
6680 } // if (n_regions > 1)
6681 // Otherwise it's just the normal boundary functions
6682 else
6683 {
6684 // Loop over all elements on boundaries
6685 nele = this->nboundary_element(b);
6686
6687 // Only bother to do anything else, if there are elements
6688 if (nele > 0)
6689 {
6690 // Check for repeated ones
6691 bool repeated = false;
6692
6693 // Loop over the bulk elements adjacent to boundary b
6694 for (unsigned e = 0; e < nele; e++)
6695 {
6696 // Get pointer to the bulk element that is adjacent to
6697 // boundary b
6698 FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
6699
6700 // Remember only to work with nonhalo elements
6701 if (bulk_elem_pt->is_halo())
6702 {
6703 n_repeated_ele++;
6704 // Skip the halo element
6705 continue;
6706 }
6707
6708 // Find the index of the face of element e along boundary b
6709 int face_index = this->face_index_at_boundary(b, e);
6710
6711 // Before adding the new element we need to be sure that the
6712 // edge that this element represents has not been already
6713 // added (only applies for internal boundaries)
6714 FiniteElement* tmp_ele_pt =
6715 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
6716
6717 const unsigned n_nodes = tmp_ele_pt->nnode();
6718
6719 std::pair<Node*, Node*> tmp_pair = std::make_pair(
6720 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
6721
6722 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
6723 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
6724
6725 // Search for repeated nodes
6726 const unsigned n_done_nodes = done_nodes_pt.size();
6727 for (unsigned l = 0; l < n_done_nodes; l++)
6728 {
6729 if (tmp_pair == done_nodes_pt[l] ||
6730 tmp_pair_inverse == done_nodes_pt[l])
6731 {
6732 // Increase the number of repeated elements
6733 n_repeated_ele++;
6734 // Mark the element as repeated
6735 repeated = true;
6736 break;
6737 }
6738 }
6739
6740 // Create new face element
6741 if (!repeated)
6742 {
6743 // Add the pair of nodes (edge) to the node dones
6744 done_nodes_pt.push_back(tmp_pair);
6745 // Add the element to the face elements
6746 face_el_pt.push_back(tmp_ele_pt);
6747 }
6748 else
6749 {
6750 // Free the repeated bulk element!!
6751 delete tmp_ele_pt;
6752 tmp_ele_pt = 0;
6753 }
6754
6755 // Re-start
6756 repeated = false;
6757
6758 } // for (e < nel)
6759 } // if (nel > 0)
6760
6761 } // else (n_regions > 1)
6762
6763 // Do not consider the repeated elements
6764 nele -= n_repeated_ele;
6765
6766#ifdef PARANOID
6767 if (nele != face_el_pt.size())
6768 {
6769 std::ostringstream error_message;
6770 error_message
6771 << "The independet counting of face elements (" << nele << ") for "
6772 << "boundary (" << b << ") is different\n"
6773 << "from the real number of face elements in the container ("
6774 << face_el_pt.size() << ")\n";
6775 //<< "Possible memory leak\n"
6776 throw OomphLibError(error_message.str(),
6777 "TriangleMesh::re_scale_re_assigned_initial_zeta_"
6778 "values_for_internal_boundary()",
6779 OOMPH_EXCEPTION_LOCATION);
6780 }
6781#endif
6782
6783 // ----------------------------------------------------------------
6784 // Second: Sort the face elements (to create segments), only
6785 // consider nonhalo elements
6786 // ----------------------------------------------------------------
6787
6788 // Get the total number of nonhalo face elements
6789 const unsigned nnon_halo_face_elements = face_el_pt.size();
6790
6791 // The vector of list to store the "segments" that compound the
6792 // boundary (segments may appear only in a distributed mesh)
6793 Vector<std::list<FiniteElement*>> segment_sorted_ele_pt;
6794
6795 // Number of already sorted face elements
6796 unsigned nsorted_face_elements = 0;
6797
6798 // Keep track of who's done
6799 std::map<FiniteElement*, bool> done_el;
6800
6801 // Keep track of which element is inverted
6802 std::map<FiniteElement*, bool> is_inverted;
6803
6804 // Iterate until all possible segments have been created
6805 while (nsorted_face_elements < nnon_halo_face_elements)
6806 {
6807 // The ordered list of face elements (in a distributed mesh a
6808 // collection of contiguous face elements define a segment)
6809 std::list<FiniteElement*> sorted_el_pt;
6810
6811#ifdef PARANOID
6812 // Select an initial element for the segment
6813 bool found_initial_face_element = false;
6814#endif
6815
6816 FiniteElement* ele_face_pt = 0;
6817
6818 unsigned iface = 0;
6819 for (iface = 0; iface < nele; iface++)
6820 {
6821 ele_face_pt = face_el_pt[iface];
6822 // If not done then take it as initial face element
6823 if (!done_el[ele_face_pt])
6824 {
6825#ifdef PARANOID
6826 found_initial_face_element = true;
6827#endif
6828 nsorted_face_elements++;
6829 iface++; // The next element number
6830 sorted_el_pt.push_back(ele_face_pt);
6831 // Mark as done
6832 done_el[ele_face_pt] = true;
6833 break;
6834 }
6835 } // for (iface < nele)
6836
6837#ifdef PARANOID
6838 if (!found_initial_face_element)
6839 {
6840 std::ostringstream error_message;
6841 error_message
6842 << "Could not find an initial face element for the current segment\n";
6843 // << "----- Possible memory leak -----\n";
6844 throw OomphLibError(error_message.str(),
6845 "TriangleMesh::re_scale_re_assigned_initial_zeta_"
6846 "values_for_internal_boundary()",
6847 OOMPH_EXCEPTION_LOCATION);
6848 }
6849#endif
6850
6851 // Number of nodes
6852 const unsigned nnod = ele_face_pt->nnode();
6853
6854 // Left and rightmost nodes (the left and right nodes of the
6855 // current face element)
6856 Node* left_node_pt = ele_face_pt->node_pt(0);
6857 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
6858
6859 // Continue iterating if a new face element has been added to the
6860 // list
6861 bool face_element_added = false;
6862
6863 // While a new face element has been added to the set of sorted
6864 // face elements then re-iterate
6865 do
6866 {
6867 // Start from the next face element since we have already added
6868 // the previous one as the initial face element (any previous
6869 // face element had to be added on previous iterations)
6870 for (unsigned iiface = iface; iiface < nele; iiface++)
6871 {
6872 // Re-start flag
6873 face_element_added = false;
6874
6875 // Get the candidate element
6876 ele_face_pt = face_el_pt[iiface];
6877
6878 // Check that the candidate element has not been done
6879 if (!(done_el[ele_face_pt]))
6880 {
6881 // Get the left and right nodes of the current element
6882 Node* local_left_node_pt = ele_face_pt->node_pt(0);
6883 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
6884
6885 // New element fits at the left of segment and is not inverted
6886 if (left_node_pt == local_right_node_pt)
6887 {
6888 left_node_pt = local_left_node_pt;
6889 sorted_el_pt.push_front(ele_face_pt);
6890 is_inverted[ele_face_pt] = false;
6891 face_element_added = true;
6892 }
6893 // New element fits at the left of segment and is inverted
6894 else if (left_node_pt == local_left_node_pt)
6895 {
6896 left_node_pt = local_right_node_pt;
6897 sorted_el_pt.push_front(ele_face_pt);
6898 is_inverted[ele_face_pt] = true;
6899 face_element_added = true;
6900 }
6901 // New element fits on the right of segment and is not inverted
6902 else if (right_node_pt == local_left_node_pt)
6903 {
6904 right_node_pt = local_right_node_pt;
6905 sorted_el_pt.push_back(ele_face_pt);
6906 is_inverted[ele_face_pt] = false;
6907 face_element_added = true;
6908 }
6909 // New element fits on the right of segment and is inverted
6910 else if (right_node_pt == local_right_node_pt)
6911 {
6912 right_node_pt = local_left_node_pt;
6913 sorted_el_pt.push_back(ele_face_pt);
6914 is_inverted[ele_face_pt] = true;
6915 face_element_added = true;
6916 }
6917
6918 if (face_element_added)
6919 {
6920 done_el[ele_face_pt] = true;
6921 nsorted_face_elements++;
6922 break;
6923 }
6924
6925 } // if (!(done_el[ele_face_pt]))
6926 } // for (iiface<nnon_halo_face_element)
6927 } while (face_element_added &&
6928 (nsorted_face_elements < nnon_halo_face_elements));
6929
6930 // Store the created segment in the vector of segments
6931 segment_sorted_ele_pt.push_back(sorted_el_pt);
6932
6933 } // while(nsorted_face_elements < nnon_halo_face_elements);
6934
6935 // --------------------------------------------------------------
6936 // Third: We have the face elements sorted, now assign boundary
6937 // coordinates to the nodes in the segments and compute the
6938 // arclength of the segment
6939 // --------------------------------------------------------------
6940
6941 // Vector of sets that stores the nodes of each segment based on a
6942 // lexicographically order starting from the bottom left node of
6943 // each segment
6944 Vector<std::set<Node*>> segment_all_nodes_pt;
6945
6946 // The number of segments in this processor
6947 const unsigned nsegments = segment_sorted_ele_pt.size();
6948
6949#ifdef PARANOID
6950 if (nnon_halo_face_elements > 0 && nsegments == 0)
6951 {
6952 std::ostringstream error_message;
6953 error_message
6954 << "The number of segments is zero, but the number of nonhalo\n"
6955 << "elements is: (" << nnon_halo_face_elements << ")\n";
6956 throw OomphLibError(error_message.str(),
6957 "TriangleMesh::re_scale_re_assigned_initial_zeta_"
6958 "values_for_internal_boundary()",
6959 OOMPH_EXCEPTION_LOCATION);
6960 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
6961#endif
6962
6963 // The arclength of each segment in the current processor
6964 Vector<double> segment_arclength(nsegments);
6965
6966 // The initial zeta for the segment
6967 Vector<double> initial_zeta_segment(nsegments);
6968
6969 // The final zeta for the segment
6970 Vector<double> final_zeta_segment(nsegments);
6971
6972 // Go through all the segments and compute the LOCAL boundary
6973 // coordinates
6974 for (unsigned is = 0; is < nsegments; is++)
6975 {
6976#ifdef PARANOID
6977 if (segment_sorted_ele_pt[is].size() == 0)
6978 {
6979 std::ostringstream error_message;
6980 error_message << "The (" << is << ")-th segment has no elements\n";
6981 throw OomphLibError(error_message.str(),
6982 "TriangleMesh::re_scale_re_assigned_initial_zeta_"
6983 "values_for_internal_boundary()",
6984 OOMPH_EXCEPTION_LOCATION);
6985 } // if (segment_sorted_ele_pt[is].size() == 0)
6986#endif
6987
6988 // Get access to the first element on the segment
6989 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
6990
6991 // Number of nodes
6992 const unsigned nnod = first_ele_pt->nnode();
6993
6994 // Get the first node of the current segment
6995 Node* first_node_pt = first_ele_pt->node_pt(0);
6996 if (is_inverted[first_ele_pt])
6997 {
6998 first_node_pt = first_ele_pt->node_pt(nnod - 1);
6999 }
7000
7001 // Get access to the last element on the segment
7002 FiniteElement* last_ele_pt = segment_sorted_ele_pt[is].back();
7003
7004 // Get the last node of the current segment
7005 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
7006 if (is_inverted[last_ele_pt])
7007 {
7008 last_node_pt = last_ele_pt->node_pt(0);
7009 }
7010
7011 // Coordinates of left node
7012 double x_left = first_node_pt->x(0);
7013 double y_left = first_node_pt->x(1);
7014
7015 // Initialise boundary coordinate (local boundary coordinate for
7016 // boundaries with more than one segment)
7017 Vector<double> zeta(1, 0.0);
7018
7019 // If we have associated a GeomObject then it is not necessary to
7020 // compute the arclength, only read the values from the nodes at
7021 // the edges
7022 if (this->boundary_geom_object_pt(b) != 0)
7023 {
7024 first_node_pt->get_coordinates_on_boundary(b, zeta);
7025 initial_zeta_segment[is] = zeta[0];
7026 last_node_pt->get_coordinates_on_boundary(b, zeta);
7027 final_zeta_segment[is] = zeta[0];
7028 }
7029
7030 // Lexicographically bottom left node
7031 std::set<Node*> local_nodes_pt;
7032 local_nodes_pt.insert(first_node_pt);
7033
7034 // Now loop over nodes in order
7035 for (std::list<FiniteElement*>::iterator it =
7036 segment_sorted_ele_pt[is].begin();
7037 it != segment_sorted_ele_pt[is].end();
7038 it++)
7039 {
7040 // Get element
7041 FiniteElement* el_pt = *it;
7042
7043 // Start node and increment
7044 unsigned k_nod = 1;
7045 int nod_diff = 1;
7046 if (is_inverted[el_pt])
7047 {
7048 k_nod = nnod - 2;
7049 nod_diff = -1;
7050 }
7051
7052 // Loop over nodes
7053 for (unsigned j = 1; j < nnod; j++)
7054 {
7055 Node* nod_pt = el_pt->node_pt(k_nod);
7056 k_nod += nod_diff;
7057
7058 // Coordinates of right node
7059 double x_right = nod_pt->x(0);
7060 double y_right = nod_pt->x(1);
7061
7062 // Increment boundary coordinate
7063 zeta[0] += sqrt((x_right - x_left) * (x_right - x_left) +
7064 (y_right - y_left) * (y_right - y_left));
7065
7066 // Increment reference coordinate
7067 x_left = x_right;
7068 y_left = y_right;
7069
7070 // Get lexicographically bottom left node but only
7071 // use vertex nodes as candidates
7072 local_nodes_pt.insert(nod_pt);
7073
7074 } // for (j < nnod)
7075 } // iterator over the elements in the segment
7076
7077 // Store the arclength of the segment
7078 segment_arclength[is] = zeta[0];
7079
7080 // Add the nodes for the corresponding segment in the container
7081 segment_all_nodes_pt.push_back(local_nodes_pt);
7082
7083 } // for (is < nsegments)
7084
7085 // ------------------------------------------------------------------
7086 // Fourth: Now we have the segments sorted, with arclength and with
7087 // LOCAL arclength assigned to the nodes. Procced to re-scale the
7088 // coordinates on the nodes based on the arclength
7089 // ------------------------------------------------------------------
7090
7091 // ------------------------------------------------------------------
7092 // Clear the original storages
7093 Boundary_segment_inverted[b].clear();
7094 Boundary_segment_initial_coordinate[b].clear();
7095 Boundary_segment_final_coordinate[b].clear();
7096
7097 Boundary_segment_initial_zeta[b].clear();
7098 Boundary_segment_final_zeta[b].clear();
7099
7100 Boundary_segment_initial_arclength[b].clear();
7101 Boundary_segment_final_arclength[b].clear();
7102
7103 // Get the zeta values for the first and last node in the boundary
7104 Vector<double> first_node_zeta_coordinate(1, 0.0);
7105 Vector<double> last_node_zeta_coordinate(1, 0.0);
7106 first_node_zeta_coordinate = boundary_initial_zeta_coordinate(b);
7107 last_node_zeta_coordinate = boundary_final_zeta_coordinate(b);
7108
7109 // Get the boundary arclength
7110 const double boundary_arclength =
7111 std::max(first_node_zeta_coordinate[0], last_node_zeta_coordinate[0]);
7112
7113 // Go through the segments and get the first and last node for each
7114 // segment
7115 for (unsigned is = 0; is < nsegments; is++)
7116 {
7117 // Get the first face element of the segment
7118 FiniteElement* first_face_ele_pt = segment_sorted_ele_pt[is].front();
7119
7120 // The number of nodes
7121 const unsigned nnod = first_face_ele_pt->nnode();
7122
7123 // ... and the first node of the segment
7124 Node* first_node_pt = first_face_ele_pt->node_pt(0);
7125 if (is_inverted[first_face_ele_pt])
7126 {
7127 first_node_pt = first_face_ele_pt->node_pt(nnod - 1);
7128 }
7129
7130 // Get the bound coordinates of the node
7131 Vector<double> zeta_first(1);
7132 first_node_pt->get_coordinates_on_boundary(b, zeta_first);
7133
7134 // Get the last face element of the segment
7135 FiniteElement* last_face_ele_pt = segment_sorted_ele_pt[is].back();
7136
7137 // ... and the last node of the segment
7138 Node* last_node_pt = last_face_ele_pt->node_pt(nnod - 1);
7139 if (is_inverted[last_face_ele_pt])
7140 {
7141 last_node_pt = last_face_ele_pt->node_pt(0);
7142 }
7143
7144 // Get the bound coordinates of the node
7145 Vector<double> zeta_last(1);
7146 last_node_pt->get_coordinates_on_boundary(b, zeta_last);
7147
7148 // Now that we have the first and last node of the segment, get
7149 // the coordinates of the nodes
7150 Vector<double> first_node_coord(2);
7151 Vector<double> last_node_coord(2);
7152 for (unsigned i = 0; i < 2; i++)
7153 {
7154 first_node_coord[i] = first_node_pt->x(i);
7155 last_node_coord[i] = last_node_pt->x(i);
7156 }
7157
7158 // Re-assign the values to identify the segments on the new mesh
7159 Boundary_segment_inverted[b].push_back(0);
7160 Boundary_segment_initial_coordinate[b].push_back(first_node_coord);
7161 Boundary_segment_final_coordinate[b].push_back(last_node_coord);
7162
7163 // Check if the boudary has an associated GeomObject
7164 if (this->boundary_geom_object_pt(b) != 0)
7165 {
7166 Boundary_segment_initial_zeta[b].push_back(zeta_first[0]);
7167 Boundary_segment_final_zeta[b].push_back(zeta_last[0]);
7168 }
7169 else
7170 {
7171 // Re-assign the values and re-scale them
7172 Boundary_segment_initial_arclength[b].push_back(zeta_first[0] *
7173 boundary_arclength);
7174 Boundary_segment_final_arclength[b].push_back(zeta_last[0] *
7175 boundary_arclength);
7176 }
7177
7178 } // for (is < nsegments)
7179
7180 // Clean all the created face elements
7181 for (unsigned i = 0; i < nele; i++)
7182 {
7183 delete face_el_pt[i];
7184 face_el_pt[i] = 0;
7185 }
7186 }
7187
7188#endif // OOMPH_HAS_MPI
7189
7190
7191#ifdef OOMPH_HAS_TRIANGLE_LIB
7192
7193 //========================================================================
7194 /// Create TriangulateIO object via the .poly file
7195 //========================================================================
7196 template<class ELEMENT>
7198 const std::string& poly_file_name,
7199 TriangulateIO& triangulate_io,
7200 bool& use_attributes)
7201 {
7202 // Process poly file
7203 // -----------------
7204 std::ifstream poly_file(poly_file_name.c_str(), std::ios_base::in);
7205 if (!poly_file)
7206 {
7207 throw OomphLibError("Error opening .poly file\n",
7208 OOMPH_CURRENT_FUNCTION,
7209 OOMPH_EXCEPTION_LOCATION);
7210 }
7211
7212 // Initialize triangulateio structure
7214
7215 // Ignore the first line with structure description
7216 poly_file.ignore(80, '\n');
7217
7218 // Read and store number of nodes
7219 unsigned invertices;
7220 poly_file >> invertices;
7221 triangulate_io.numberofpoints = invertices;
7222
7223 // Initialisation of the point list
7224 triangulate_io.pointlist =
7225 (double*)malloc(triangulate_io.numberofpoints * 2 * sizeof(double));
7226
7227 // Read and store spatial dimension of nodes
7228 unsigned mesh_dim;
7229 poly_file >> mesh_dim;
7230
7231 if (mesh_dim == 0)
7232 {
7233 mesh_dim = 2;
7234 }
7235
7236#ifdef PARANOID
7237 if (mesh_dim != 2)
7238 {
7239 throw OomphLibError("The dimension must be 2\n",
7240 OOMPH_CURRENT_FUNCTION,
7241 OOMPH_EXCEPTION_LOCATION);
7242 }
7243#endif
7244
7245 // Read and check the flag for attributes
7246 unsigned nextras;
7247 poly_file >> nextras;
7248
7249 triangulate_io.numberofpointattributes = 0;
7250 triangulate_io.pointattributelist = (double*)NULL;
7251
7252 // Read and check the flag for boundary markers
7253 unsigned nodemarkers;
7254 poly_file >> nodemarkers;
7255 triangulate_io.pointmarkerlist = (int*)NULL;
7256
7257#ifdef PARANOID
7258 // Reading the .poly with the oomph.lib we need
7259 // to set the point attribute and markers to 0
7260 if (nextras != 0 || nodemarkers != 0)
7261 {
7262 oomph_info << "===================================================="
7263 << std::endl
7264 << std::endl;
7265 oomph_info << "Reading the .poly file via oomph_lib \n"
7266 << "point's attribute and point's markers \n"
7267 << "are automatically set to 0" << std::endl;
7268 oomph_info << "===================================================="
7269 << std::endl;
7270 }
7271#endif
7272
7273 // Dummy for node number (and attribute or markers if included)
7274 unsigned dummy_value;
7275 unsigned count_point = 0;
7276 std::string test_string;
7277
7278 // Skip line with commentary
7279 getline(poly_file, test_string, '#');
7280 poly_file.ignore(80, '\n');
7281
7282 // Read and store all the nodes coordinates
7283 // (hole's vertices as well)
7284 for (unsigned count = 0; count < invertices; count++)
7285 {
7286 poly_file >> dummy_value;
7287 poly_file >> triangulate_io.pointlist[count_point];
7288 poly_file >> triangulate_io.pointlist[count_point + 1];
7289 if (nextras != 0 || nodemarkers != 0)
7290 {
7291 for (unsigned j = 0; j < nextras; j++)
7292 {
7293 poly_file >> dummy_value;
7294 }
7295 }
7296 else if (nextras != 0 && nodemarkers != 0)
7297 {
7298 for (unsigned j = 0; j < nextras; j++)
7299 {
7300 poly_file >> dummy_value;
7301 poly_file >> dummy_value;
7302 }
7303 }
7304 // Read the next line
7305 poly_file.ignore(80, '\n');
7306
7307 // Skip line with commentary for internal box whether found
7308 if (poly_file.get() == '#')
7309 {
7310 poly_file.ignore(80, '\n');
7311 }
7312 // If read the char should be put back in the string
7313
7314 else
7315 {
7316 poly_file.unget();
7317 }
7318 count_point += 2;
7319 }
7320
7321 // The line with the segment's commentary has been skipped
7322 // by the command of the last loop
7323
7324 // Read and store the number of segments
7325 unsigned dummy_seg;
7326 unsigned inelements;
7327 poly_file >> inelements;
7328
7329 unsigned segment_markers;
7330 poly_file >> segment_markers;
7331
7332 // Marker list should be provided by the user to assign
7333 // each segment to a boundary
7334#ifdef PARANOID
7335 if (segment_markers != 1)
7336 {
7337 std::ostringstream error_stream;
7338 error_stream << "The segment marker should be provided \n"
7339 << "In order to assign each segment to a boundary \n "
7340 << std::endl;
7341
7342 throw OomphLibError(
7343 error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
7344 }
7345#endif
7346
7347 triangulate_io.numberofsegments = inelements;
7348 triangulate_io.segmentlist =
7349 (int*)malloc(triangulate_io.numberofsegments * 2 * sizeof(int));
7350 triangulate_io.segmentmarkerlist =
7351 (int*)malloc(triangulate_io.numberofsegments * sizeof(int));
7352
7353 // Read all the segments edges and markers
7354 for (unsigned i = 0; i < 2 * inelements; i += 2)
7355 {
7356 poly_file >> dummy_seg;
7357 poly_file >> triangulate_io.segmentlist[i];
7358 poly_file >> triangulate_io.segmentlist[i + 1];
7359 if (segment_markers != 0)
7360 {
7361 poly_file >> triangulate_io.segmentmarkerlist[i / 2];
7362 }
7363
7364 // Skip line with commentary
7365 poly_file.ignore(80, '\n');
7366 }
7367
7368 // Read and store the number of holes if given
7369 // Skip line with commentary
7370 if (getline(poly_file, test_string, '#'))
7371 {
7372 poly_file.ignore(80, '\n');
7373
7374 unsigned dummy_hole;
7375 unsigned nhole;
7376 poly_file >> nhole;
7377
7378 triangulate_io.numberofholes = nhole;
7379 triangulate_io.holelist =
7380 (double*)malloc(triangulate_io.numberofholes * 2 * sizeof(double));
7381
7382 // Loop over the holes to get centre coords and store value onto the
7383 // TriangulateIO object
7384 for (unsigned i = 0; i < 2 * nhole; i += 2)
7385 {
7386 poly_file >> dummy_hole;
7387 poly_file >> triangulate_io.holelist[i];
7388 poly_file >> triangulate_io.holelist[i + 1];
7389 }
7390 }
7391
7392 // Read and store the number of regions if given
7393 // Skip line with commentary
7394 if (getline(poly_file, test_string, '#'))
7395 {
7396 poly_file.ignore(80, '\n');
7397
7398 unsigned dummy_region;
7399 unsigned nregion;
7400 poly_file >> nregion;
7401 std::cerr << "Regions: " << nregion << std::endl;
7402 getchar();
7403
7404 triangulate_io.numberofregions = nregion;
7405 triangulate_io.regionlist =
7406 (double*)malloc(triangulate_io.numberofregions * 4 * sizeof(double));
7407
7408 // Check for using regions
7409 if (nregion > 0)
7410 {
7411 use_attributes = true;
7412 }
7413
7414 // Loop over the regions to get coords and store value onto the
7415 // TriangulateIO object
7416 for (unsigned i = 0; i < nregion; i++)
7417 {
7418 poly_file >> dummy_region;
7419 poly_file >> triangulate_io.regionlist[4 * i];
7420 poly_file >> triangulate_io.regionlist[4 * i + 1];
7421 poly_file >> triangulate_io.regionlist[4 * i + 2];
7422 triangulate_io.regionlist[4 * i + 3] = 0.0;
7423 }
7424 }
7425 }
7426
7427#endif
7428
7429#ifdef OOMPH_HAS_TRIANGLE_LIB
7430#ifdef OOMPH_HAS_MPI
7431
7432 //======================================================================
7433 /// Used to dump info. related with distributed triangle meshes
7434 //======================================================================
7435 template<class ELEMENT>
7437 std::ostream& dump_file)
7438 {
7439 // First check that the mesh is distributed
7440 if (this->is_mesh_distributed())
7441 {
7442 // Save the original number of boundaries
7443 const unsigned nboundary = this->nboundary();
7444 dump_file << nboundary << " # number of original boundaries" << std::endl;
7445
7446 // Save the number of shared boundaries
7447 const unsigned nshared_boundaries = this->nshared_boundaries();
7448 dump_file << nshared_boundaries << " # number of shared boundaries"
7449 << std::endl;
7450
7451 // Save the initial and final shared boundaries ids
7452 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
7453 dump_file << init_shd_bnd_id << " # initial shared boundaries id"
7454 << std::endl;
7455
7456 const unsigned final_shd_bnd_id = this->final_shared_boundary_id();
7457 dump_file << final_shd_bnd_id << " # final shared boundaries id"
7458 << std::endl;
7459
7460 // Save the number of processors
7461 const unsigned nprocs = this->shared_boundaries_ids().size();
7462 dump_file << nprocs << " # number of processors" << std::endl;
7463
7464 // Now save the processors ids and the shared boundary created
7465 // by them
7466 for (unsigned ip = 0; ip < nprocs; ip++)
7467 {
7468 for (unsigned jp = 0; jp < nprocs; jp++)
7469 {
7470 if (ip != jp)
7471 {
7472 // Get the number of shared boundaries with it these two
7473 // processors
7474 const unsigned nshared_boundaries_iproc_jproc =
7475 this->shared_boundaries_ids(ip, jp).size();
7476
7477 // Save the number of shared boundaries with in these two
7478 // processors
7479 dump_file << nshared_boundaries_iproc_jproc
7480 << " # number of shared boundaries with in two "
7481 << "processors" << std::endl;
7482 for (unsigned is = 0; is < nshared_boundaries_iproc_jproc; is++)
7483 {
7484 const unsigned shared_boundary_id =
7485 this->shared_boundaries_ids(ip, jp, is);
7486 dump_file << ip << " " << jp << " " << shared_boundary_id
7487 << " # ip jp shared_boundary of processors ip and jp"
7488 << std::endl;
7489
7490 } // for (is < nshared_boundaries_iproc_jproc)
7491 }
7492 } // for (jp < nprocs)
7493 } // for (ip < nprocs)
7494
7495 // Now save the info. that states which shared boundary overlaps
7496 // an internal boundary
7497
7498 // First check if there are shared boundaries overlapping internal
7499 // boundaries
7500 const unsigned nshared_boundaries_overlap_internal_boundaries =
7501 this->nshared_boundary_overlaps_internal_boundary();
7502 dump_file << nshared_boundaries_overlap_internal_boundaries
7503 << " # number of shared boundaries that overlap internal "
7504 << "boundaries" << std::endl;
7505
7506 if (nshared_boundaries_overlap_internal_boundaries > 0)
7507 {
7508 for (unsigned isb = init_shd_bnd_id; isb < final_shd_bnd_id; isb++)
7509 {
7510 // Check if the current shared boundary overlaps an internal
7511 // boundary
7512 if (this->shared_boundary_overlaps_internal_boundary(isb))
7513 {
7514 // Which internal boundary is overlapped by the shared
7515 // boundary
7516 const unsigned overlapped_internal_boundary =
7517 shared_boundary_overlapping_internal_boundary(isb);
7518 // Save the shared boundary that overlaps the internal boundary
7519 dump_file << isb << " " << overlapped_internal_boundary
7520 << " # the shared boundary overlaps the internal "
7521 << "boundary " << std::endl;
7522
7523 } // if (this->shared_boundary_overlaps_internal_boundary(isb))
7524 } // for (isb < final_shd_bnd_id)
7525 } // if (nshared_boundaries_overlap_internal_boundaries > 0)
7526
7527 // Now save the info. related with the initial and final
7528 // boundary coordinates for each original boundary
7529
7530 // Go through all the (original) boundaries to update the initial
7531 // and final boundary coordinates
7532 for (unsigned b = 0; b < nboundary; b++)
7533 {
7534 // Check if the boundary zeta coordinates for this boundary have
7535 // been already assigned, if that is the case then state the
7536 // flag to know that info. should be read
7537 if (Assigned_segments_initial_zeta_values[b])
7538 {
7539 // The boundary coordinates have been computed then state
7540 // the flag and save the info.
7541 dump_file << "1 # assigned boundary coordinates initial zeta values"
7542 << std::endl;
7543
7544 // Save the initial and final boundary coordinates, same as
7545 // the initial and final zeta values for each boundary
7546
7547 // First the vertices coordinates
7548 Vector<double> initial_coordinates =
7549 this->boundary_initial_coordinate(b);
7550
7551 Vector<double> final_coordinates = this->boundary_final_coordinate(b);
7552
7553 dump_file << std::setprecision(14) << initial_coordinates[0] << " "
7554 << initial_coordinates[1]
7555 << " # initial coordinates for the current boundary"
7556 << std::endl;
7557
7558 dump_file << std::setprecision(14) << final_coordinates[0] << " "
7559 << final_coordinates[1]
7560 << " # final coordinates for the current boundary"
7561 << std::endl;
7562
7563 // ... then the zeta values
7564
7565#ifdef PARANOID
7566 // Get the number of zeta coordinates (should be one)
7567 const unsigned zeta_size =
7568 this->boundary_initial_zeta_coordinate(b).size();
7569
7570 if (zeta_size != 1)
7571 {
7572 std::ostringstream error_message;
7573 error_message
7574 << "The dimension for the zeta values container is different\n"
7575 << "from 1, the current implementation only supports\n"
7576 << "one-dimensioned zeta containers\n\n";
7577 throw OomphLibError(
7578 error_message.str(),
7579 "TriangleMesh::dump_distributed_info_for_restart()",
7580 OOMPH_EXCEPTION_LOCATION);
7581 }
7582#endif
7583
7584 Vector<double> zeta_initial =
7585 this->boundary_initial_zeta_coordinate(b);
7586 Vector<double> zeta_final = this->boundary_final_zeta_coordinate(b);
7587
7588 dump_file << std::setprecision(14) << zeta_initial[0]
7589 << " # initial zeta value for the current boundary"
7590 << std::endl;
7591
7592 dump_file << std::setprecision(14) << zeta_final[0]
7593 << " # final zeta value for the current boundary"
7594 << std::endl;
7595
7596 // Get the number of segments of the current boundary
7597 const unsigned nsegments = this->nboundary_segment(b);
7598 // Save the number of segments of the current boundary
7599 dump_file << b << " " << nsegments
7600 << " # of segments for the current boundary" << std::endl;
7601
7602 // ... and then save that info for each segments
7603 for (unsigned is = 0; is < nsegments; is++)
7604 {
7605 // First the vertices coordinates
7606 Vector<double> initial_segment_coordinates =
7607 this->boundary_segment_initial_coordinate(b)[is];
7608 Vector<double> final_segment_coordinates =
7609 this->boundary_segment_final_coordinate(b)[is];
7610
7611 dump_file
7612 << std::setprecision(14) << initial_segment_coordinates[0] << " "
7613 << initial_segment_coordinates[1]
7614 << " # initial segment coordinates for the current boundary"
7615 << std::endl;
7616
7617 dump_file << std::setprecision(14) << final_segment_coordinates[0]
7618 << " " << final_segment_coordinates[1]
7619 << " # final segment coordinates for the current boundary"
7620 << std::endl;
7621
7622 // ... then the zeta values
7623
7624 if (this->boundary_geom_object_pt(b) != 0)
7625 {
7626 const double zeta_segment_initial =
7627 this->boundary_segment_initial_zeta(b)[is];
7628 const double zeta_segment_final =
7629 this->boundary_segment_final_zeta(b)[is];
7630
7631 dump_file
7632 << std::setprecision(14) << zeta_segment_initial
7633 << " # initial segment zeta value for the current boundary"
7634 << std::endl;
7635
7636 dump_file
7637 << std::setprecision(14) << zeta_segment_final
7638 << " # final segment zeta value for the current boundary"
7639 << std::endl;
7640 }
7641 else
7642 {
7643 const double arclength_segment_initial =
7644 this->boundary_segment_initial_arclength(b)[is];
7645 const double arclength_segment_final =
7646 this->boundary_segment_final_arclength(b)[is];
7647
7648 dump_file
7649 << std::setprecision(14) << arclength_segment_initial
7650 << " # initial segment arclength for the current boundary"
7651 << std::endl;
7652
7653 dump_file << std::setprecision(14) << arclength_segment_final
7654 << " # final segment arclength for the current boundary"
7655 << std::endl;
7656
7657 } // else if (this->boundary_geom_object_pt(b)!=0)
7658
7659 } // for (is < nsegments)
7660
7661 } // if (Assigned_segments_initial_zeta_values[b])
7662 else
7663 {
7664 // The boundary coordinates have NOT been computed then state
7665 // the flag and save the info.
7666 dump_file << "0 # assigned boundary coordinates initial zeta values"
7667 << std::endl;
7668 }
7669
7670 } // for (b < nboundary)
7671
7672 } // if (this->is_mesh_distributed())
7673 }
7674
7675 //======================================================================
7676 /// Used to read info. related with distributed triangle meshes
7677 //======================================================================
7678 template<class ELEMENT>
7680 std::istream& restart_file)
7681 {
7682 // First check that the mesh is distributed
7683 if (this->is_mesh_distributed())
7684 {
7685 // Read the number of original boundaries
7686 const unsigned n_boundary = read_unsigned_line_helper(restart_file);
7687
7688#ifdef PARANOID
7689 if (n_boundary != this->nboundary())
7690 {
7691 std::ostringstream error_message;
7692 error_message
7693 << "The number of boundaries (" << n_boundary << ") on the "
7694 << "file used for restarting is different\nfrom the number of "
7695 << "boundaries (" << this->nboundary() << ") on the current "
7696 << "mesh!!!\n\n\n";
7697 throw OomphLibError(error_message.str(),
7698 "TriangleMesh::read_distributed_info_for_restart()",
7699 OOMPH_EXCEPTION_LOCATION);
7700 }
7701#endif
7702
7703 // Read the number of shared boundaries
7704 unsigned n_shared_boundaries = read_unsigned_line_helper(restart_file);
7705 // We need to read the data because it comes in the file (add and
7706 // substract to avoid compilation warning)
7707 n_shared_boundaries++;
7708 n_shared_boundaries--;
7709
7710 // Read the initial and final shared boundaries ids
7711 unsigned init_shd_bnd_id = read_unsigned_line_helper(restart_file);
7712 // We need to read the data because it comes in the file (add and
7713 // substract to avoid compilation warning)
7714 init_shd_bnd_id++;
7715 init_shd_bnd_id--;
7716 // Add and substract to avoid compilation warning
7717 unsigned final_shd_bnd_id = read_unsigned_line_helper(restart_file);
7718 // We need to read the data because it comes in the file (add and
7719 // substract to avoid compilation warning)
7720 final_shd_bnd_id++;
7721 final_shd_bnd_id--;
7722
7723 // Read the number of processors involved in the generation of
7724 // mesh before restart
7725 const unsigned n_procs = read_unsigned_line_helper(restart_file);
7726
7727#ifdef PARANOID
7728 if (static_cast<int>(n_procs) != this->communicator_pt()->nproc())
7729 {
7730 std::ostringstream error_message;
7731 error_message
7732 << "The number of previously used processors (" << n_procs
7733 << ") (read from the restart file) is different\nfrom the "
7734 << "number of current used processors ("
7735 << this->communicator_pt()->nproc() << ")\n\n";
7736 throw OomphLibError(error_message.str(),
7737 "TriangleMesh::read_distributed_info_for_restart()",
7738 OOMPH_EXCEPTION_LOCATION);
7739 }
7740#endif
7741
7742 // Clear all previuos info. related with shared boundaries
7743 this->shared_boundaries_ids().clear();
7744 this->shared_boundary_from_processors().clear();
7745 this->shared_boundary_overlaps_internal_boundary().clear();
7746
7747 // Create the storage for the shared boundaries ids related with
7748 // the processors
7749 this->shared_boundaries_ids().resize(n_procs);
7750
7751 // Now read the processors ids and the shared boundary created
7752 // by them
7753 for (unsigned ip = 0; ip < n_procs; ip++)
7754 {
7755 // Create the storage for the shared boundaries ids related with
7756 // the processors
7757 this->shared_boundaries_ids(ip).resize(n_procs);
7758 for (unsigned jp = 0; jp < n_procs; jp++)
7759 {
7760 if (ip != jp)
7761 {
7762 // Read the number of shared boundaries with in these two
7763 // processors
7764 const unsigned nshared_boundaries_iproc_jproc =
7765 read_unsigned_line_helper(restart_file);
7766 for (unsigned is = 0; is < nshared_boundaries_iproc_jproc; is++)
7767 {
7768 // Get the processors
7769 unsigned tmp_ip;
7770 restart_file >> tmp_ip;
7771 unsigned tmp_jp;
7772 restart_file >> tmp_jp;
7773
7774 // Get the shared boundary id created by these two
7775 // processors
7776 const unsigned shared_boundary_id =
7777 read_unsigned_line_helper(restart_file);
7778
7779 // Update the info. of the processors that give rise to
7780 // the shared boundaries
7781 this->shared_boundaries_ids(ip, jp).push_back(shared_boundary_id);
7782
7783 // Update the structure that states the processors that
7784 // gave rise to the shared boundary
7785 Vector<unsigned> processors(2);
7786 processors[0] = ip;
7787 processors[1] = jp;
7788 this->shared_boundary_from_processors()[shared_boundary_id] =
7789 processors;
7790
7791 } // for (is < nshared_boundaries_iproc_jproc)
7792 }
7793 } // for (jp < n_procs)
7794 } // for (ip < n_procs)
7795
7796 // Now read the info. that states which shared boundary overlaps
7797 // an internal boundary
7798
7799 // First check if there are shared boundaries overlapping internal
7800 // boundaries
7801 const unsigned nshared_boundaries_overlap_internal_boundaries =
7802 read_unsigned_line_helper(restart_file);
7803
7804 for (unsigned isb = 0;
7805 isb < nshared_boundaries_overlap_internal_boundaries;
7806 isb++)
7807 {
7808 // Read the shared boundary that overlaps an internal boundary
7809 unsigned shared_boundary_overlapping;
7810 restart_file >> shared_boundary_overlapping;
7811 // ... and read the internal boundary that overlaps
7812 const unsigned overlapped_internal_boundary =
7813 read_unsigned_line_helper(restart_file);
7814
7815 // Re-establish the info. of the shared boundaries overlapped
7816 // by internal boundaries
7817 this->shared_boundary_overlaps_internal_boundary()
7818 [shared_boundary_overlapping] = overlapped_internal_boundary;
7819 } // for (isb < nshared_boundaries_overlap_internal_boundaries)
7820
7821 // Now read the info. related with the initial and final
7822 // boundary coordinates for each original boundary
7823
7824 // Go through all the (original) boundaries to update the initial
7825 // and final boundary coordinates
7826 for (unsigned b = 0; b < n_boundary; b++)
7827 {
7828 // For each boundary check if the boundary coordinates initial
7829 // and final zeta vales were assigned in the restart file
7830 const unsigned boundary_coordinates_initial_zeta_values_assigned =
7831 read_unsigned_line_helper(restart_file);
7832
7833 if (boundary_coordinates_initial_zeta_values_assigned)
7834 {
7835 // Clear any previous stored info. There should not be
7836 // info. already stored but better clear the info. for the
7837 // boundary
7838 Boundary_initial_coordinate[b].clear();
7839 Boundary_final_coordinate[b].clear();
7840
7841 Boundary_initial_zeta_coordinate[b].clear();
7842 Boundary_final_zeta_coordinate[b].clear();
7843
7844 // The info. for the segments
7845 Boundary_segment_inverted[b].clear();
7846 Boundary_segment_initial_coordinate[b].clear();
7847 Boundary_segment_final_coordinate[b].clear();
7848
7849 Boundary_segment_initial_zeta[b].clear();
7850 Boundary_segment_final_zeta[b].clear();
7851
7852 Boundary_segment_initial_arclength[b].clear();
7853 Boundary_segment_final_arclength[b].clear();
7854
7855 // Read the initial and final boundary coordinates, same as
7856 // the initial and final zeta values for each boundary
7857
7858 // First the vertices coordinates
7859 Vector<double> initial_coordinates(2);
7860
7861 // Read the initial coordinates
7862 restart_file >> initial_coordinates[0] >> initial_coordinates[1];
7863
7864 // Ignore rest of line
7865 restart_file.ignore(80, '\n');
7866
7867 Vector<double> final_coordinates(2);
7868
7869 // Read the final coordinates
7870 restart_file >> final_coordinates[0] >> final_coordinates[1];
7871
7872 // Ignore rest of line
7873 restart_file.ignore(80, '\n');
7874
7875 // Set the values in the containers
7876
7877
7878 this->boundary_initial_coordinate(b) = initial_coordinates;
7879 this->boundary_final_coordinate(b) = final_coordinates;
7880
7881 // ... now read the zeta values
7882 Vector<double> zeta_initial(1);
7883 restart_file >> zeta_initial[0];
7884
7885 // Ignore rest of line
7886 restart_file.ignore(80, '\n');
7887
7888 Vector<double> zeta_final(1);
7889 restart_file >> zeta_final[0];
7890
7891 // Ignore rest of line
7892 restart_file.ignore(80, '\n');
7893
7894 // Set the values in the containers
7895 this->boundary_initial_zeta_coordinate(b) = zeta_initial;
7896 this->boundary_final_zeta_coordinate(b) = zeta_final;
7897
7898 // Get the curent boundary id from the restart file
7899 unsigned current_boundary;
7900 restart_file >> current_boundary;
7901
7902#ifdef PARANOID
7903 if (current_boundary != b)
7904 {
7905 std::ostringstream error_message;
7906 error_message
7907 << "The current boundary id from the restart file ("
7908 << current_boundary << ") is different from\nthe boundary id "
7909 << b << "currently used to re-establish the initial and\nfinal "
7910 << "segment's zeta values\n\n";
7911 throw OomphLibError(
7912 error_message.str(),
7913 "TriangleMesh::read_distributed_info_for_restart()",
7914 OOMPH_EXCEPTION_LOCATION);
7915 }
7916#endif
7917
7918 // ... and its number of segments
7919 unsigned nsegments;
7920 restart_file >> nsegments;
7921
7922 // Ignore rest of line
7923 restart_file.ignore(80, '\n');
7924
7925 // Now read all the segments info.
7926
7927 // ... and then save that info for each segments
7928 for (unsigned is = 0; is < nsegments; is++)
7929 {
7930 // First the vertices coordinates
7931 Vector<double> initial_segment_coordinates(2);
7932
7933 // Read the initial coordinates
7934 restart_file >> initial_segment_coordinates[0] >>
7935 initial_segment_coordinates[1];
7936
7937 // Ignore rest of line
7938 restart_file.ignore(80, '\n');
7939
7940 Vector<double> final_segment_coordinates(2);
7941
7942 // Read the final coordinates
7943 restart_file >> final_segment_coordinates[0] >>
7944 final_segment_coordinates[1];
7945
7946 // Ignore rest of line
7947 restart_file.ignore(80, '\n');
7948
7949 // Set the values in the containers
7950 this->boundary_segment_initial_coordinate(b).push_back(
7951 initial_segment_coordinates);
7952 this->boundary_segment_final_coordinate(b).push_back(
7953 final_segment_coordinates);
7954
7955 // ... then the zeta values for the segment
7956 if (this->boundary_geom_object_pt(b) != 0)
7957 {
7958 Vector<double> zeta_segment_initial(1);
7959 restart_file >> zeta_segment_initial[0];
7960
7961 // Ignore rest of line
7962 restart_file.ignore(80, '\n');
7963
7964 Vector<double> zeta_segment_final(1);
7965 restart_file >> zeta_segment_final[0];
7966
7967 // Ignore rest of line
7968 restart_file.ignore(80, '\n');
7969
7970 // Set the values in the containers for the segment
7971 this->boundary_segment_initial_zeta(b).push_back(
7972 zeta_segment_initial[0]);
7973 this->boundary_segment_final_zeta(b).push_back(
7974 zeta_segment_final[0]);
7975 }
7976 else
7977 {
7978 Vector<double> arclength_segment_initial(1);
7979 restart_file >> arclength_segment_initial[0];
7980
7981 // Ignore rest of line
7982 restart_file.ignore(80, '\n');
7983
7984 Vector<double> arclength_segment_final(1);
7985 restart_file >> arclength_segment_final[0];
7986
7987 // Ignore rest of line
7988 restart_file.ignore(80, '\n');
7989
7990 // Set the values in the containers for the segment
7991 this->boundary_segment_initial_arclength(b).push_back(
7992 arclength_segment_initial[0]);
7993 this->boundary_segment_final_arclength(b).push_back(
7994 arclength_segment_final[0]);
7995 } // else if (this->boundary_geom_object_pt(b)!=0)
7996
7997 } // for (is < nsegments)
7998
7999 } // if (boundary_coordinates_initial_zeta_values_assigned)
8000
8001 } // for (b < n_boundary)
8002
8003 } // if (this->is_mesh_distributed())
8004 }
8005
8006#endif // #ifdef OOMPH_HAS_MPI
8007#endif // #ifdef OOMPH_HAS_TRIANGLE_LIB
8008
8009 //===================================================================
8010 // Output the nodes on the boundaries and their / respective boundary
8011 // coordinates(into separate tecplot / zones)
8012 //===================================================================
8013 template<class ELEMENT>
8015 std::ostream& outfile)
8016 {
8017 // First get all the elements adjacent to the given boundary, then
8018 // the face elements and extract the nodes on the boundaries using
8019 // the face elements. We can not use the data structure
8020 // Boundary_node_pt since the multi_domain functions add nodes there
8021 // without assigning the required boundary coordinate
8022
8023 // Store the nodes in a set so we do not have repeated nodes
8024 std::set<Node*> boundary_nodes_pt;
8025 const unsigned n_boundary_ele = this->nboundary_element(b);
8026 for (unsigned e = 0; e < n_boundary_ele; e++)
8027 {
8028 // Get the boundary bulk element
8029 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
8030#ifdef OOMPH_HAS_MPI
8031 // Only work with nonhalo elements if the mesh is distributed
8032 if (!bulk_ele_pt->is_halo())
8033 {
8034#endif
8035 // Get the face index
8036 int face_index = this->face_index_at_boundary(b, e);
8037 // Create the face element
8038 FiniteElement* face_ele_pt =
8039 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
8040
8041 // Get the number of nodes on the face element
8042 const unsigned n_nodes = face_ele_pt->nnode();
8043 for (unsigned i = 0; i < n_nodes; i++)
8044 {
8045 // Get the nodes in the face elements
8046 Node* tmp_node_pt = face_ele_pt->node_pt(i);
8047 // Add the nodes to the set of boundary nodes
8048 boundary_nodes_pt.insert(tmp_node_pt);
8049 } // for (i < n_nodes)
8050
8051 // Free the memory allocated for the face element
8052 delete face_ele_pt;
8053 face_ele_pt = 0;
8054#ifdef OOMPH_HAS_MPI
8055 } // if (!bulk_ele_pt->is_halo())
8056#endif
8057
8058 } // for (e < n_boundary_ele)
8059
8060 outfile << "ZONE T=\"Boundary nodes" << b << "\"\n";
8061 // Set to store the boundary nodes in order
8062 std::set<Vector<double>> set_node_coord;
8063 // Loop over the nodes on the boundary and store them in the set
8064 for (std::set<Node*>::iterator it = boundary_nodes_pt.begin();
8065 it != boundary_nodes_pt.end();
8066 it++)
8067 {
8068 Node* inode_pt = (*it);
8069
8070 // Get the node coordinates
8071 const unsigned n_dim = inode_pt->ndim();
8072 Vector<double> node_coord(n_dim + 1);
8073
8074 // Get the boundary coordinate
8075 Vector<double> zeta(1);
8076 inode_pt->get_coordinates_on_boundary(b, zeta);
8077 node_coord[0] = zeta[0];
8078 for (unsigned j = 0; j < n_dim; j++)
8079 {
8080 node_coord[j + 1] = inode_pt->x(j);
8081 }
8082 set_node_coord.insert(node_coord);
8083 }
8084
8085 for (std::set<Vector<double>>::iterator it = set_node_coord.begin();
8086 it != set_node_coord.end();
8087 it++)
8088 {
8089 // Get the node coordinates
8090 Vector<double> node_coord = (*it);
8091
8092 // Output the node coordinates
8093 const unsigned n_dim = node_coord.size() - 1;
8094 for (unsigned j = 0; j < n_dim; j++)
8095 {
8096 outfile << node_coord[j + 1] << " ";
8097 }
8098 // ... add an extra coordinate to avoid error with tecplot
8099 outfile << "0.0" << std::endl;
8100 }
8101
8102 // ... loop again to plot the bound coordinates
8103 outfile << "ZONE T=\"Boundary coordinates " << b << "\"\n";
8104 for (std::set<Vector<double>>::iterator it = set_node_coord.begin();
8105 it != set_node_coord.end();
8106 it++)
8107 {
8108 // Get the node coordinates
8109 Vector<double> node_coord = (*it);
8110
8111 // Output the node coordinates
8112 const unsigned n_dim = node_coord.size() - 1;
8113 for (unsigned j = 0; j < n_dim; j++)
8114 {
8115 outfile << node_coord[j + 1] << " ";
8116 }
8117
8118 // Output the boundary coordinate
8119 outfile << node_coord[0] << std::endl;
8120 }
8121 }
8122
8123#ifdef OOMPH_HAS_MPI
8124 //====================================================================
8125 // Creates the distributed domain representation. Joins the
8126 // original boundaires, shared boundaries and creates connections among
8127 // them to create the new polygons that represent the distributed
8128 // domain
8129 //====================================================================
8130 template<class ELEMENT>
8132 Vector<TriangleMeshPolygon*>& polygons_pt,
8133 Vector<TriangleMeshOpenCurve*>& open_curves_pt)
8134 {
8135 // Get the outer polygons, internal polygons, internal open curves
8136 // and join them with the shared polylines to create the distributed
8137 // domain representation (the new outer, internal polygons, and new
8138 // internal open curves)
8139
8140 // Get the rank of the current processor
8141 const unsigned my_rank = this->communicator_pt()->my_rank();
8142
8143 // *********************************************************************
8144 // Step (2) Get the outer, internal and shared boundaries to create the
8145 // new polygons
8146 // *********************************************************************
8147
8148 // *********************************************************************
8149 // Step (2.1) Get the outer boundaries and check if it is necessary to use
8150 // a new representation (for example when the boundary was splitted in
8151 // the distribution process)
8152 // *********************************************************************
8153
8154 // Storage for new created polylines, non sorted
8155 Vector<TriangleMeshPolyLine*> unsorted_outer_polyline_pt;
8156
8157 // Storing for the polylines on the boundaries
8158 // The first index is for a set of connected polylines
8159 // The second index is for a polyline on a set of connected polylines
8160 Vector<Vector<TriangleMeshPolyLine*>> sorted_outer_curves_pt;
8161
8162 // Copy the outer boundaries to the vector of polylines
8163 const unsigned nouter = this->Outer_boundary_pt.size();
8164 for (unsigned i = 0; i < nouter; i++)
8165 {
8166 const unsigned npolylines = this->Outer_boundary_pt[i]->npolyline();
8167 for (unsigned p = 0; p < npolylines; p++)
8168 {
8169 // Pointer to the current polyline
8170 TriangleMeshPolyLine* tmp_polyline_pt =
8171 this->Outer_boundary_pt[i]->polyline_pt(p);
8172 const unsigned nvertex = tmp_polyline_pt->nvertex();
8173 if (nvertex > 0)
8174 {
8175 // Get the boundary id of the polyline and check if that boundary
8176 // needs a new representation (for example when the boundary was
8177 // splitted in the distribution process)
8178 const unsigned bound_id = tmp_polyline_pt->boundary_id();
8179 if (!boundary_was_splitted(bound_id))
8180 {
8181 unsorted_outer_polyline_pt.push_back(tmp_polyline_pt);
8182 } // if (!boundary_was_splitted(bound_id))
8183 else
8184 {
8185 // Get the polylines that will represent this boundary
8186 Vector<TriangleMeshPolyLine*> tmp_vector_polylines =
8187 boundary_subpolylines(bound_id);
8188 const unsigned nsub_poly = tmp_vector_polylines.size();
8189#ifdef PARANOID
8190 if (nsub_poly <= 1)
8191 {
8192 std::ostringstream error_message;
8193 error_message << "The boundary (" << bound_id
8194 << ") was marked to be splitted but\n"
8195 << "there are only (" << nsub_poly
8196 << ") polylines to represent it.\n";
8197 throw OomphLibError(error_message.str(),
8198 OOMPH_CURRENT_FUNCTION,
8199 OOMPH_EXCEPTION_LOCATION);
8200 }
8201#endif
8202 // Add the new representation of the polylines (sub-polylines)
8203 // to represent this boundary
8204 for (unsigned isub = 0; isub < nsub_poly; isub++)
8205 {
8206 unsorted_outer_polyline_pt.push_back(tmp_vector_polylines[isub]);
8207#ifdef PARANOID
8208 const unsigned nsvertex = tmp_vector_polylines[isub]->nvertex();
8209 if (nsvertex == 0)
8210 {
8211 std::ostringstream error_message;
8212 error_message
8213 << "The current chunk (" << isub << ") of the polyline with\n"
8214 << "boundary id (" << bound_id << ") has no vertices\n";
8215 throw OomphLibError(error_message.str(),
8216 OOMPH_CURRENT_FUNCTION,
8217 OOMPH_EXCEPTION_LOCATION);
8218 } // if (nsvertex == 0)
8219#endif // #ifdef PARANOID
8220 } // for (isub < nsub_poly)
8221 } // else if (!boundary_was_splitted(bound_id))
8222 } // if (nvertex > 0)
8223 } // for (p < npolylines)
8224 } // for (i < nouter)
8225
8226 // Get the number of unsorted polylines
8227 unsigned nunsorted_outer_polyline = unsorted_outer_polyline_pt.size();
8228 if (nunsorted_outer_polyline > 0)
8229 {
8230 // Now that we have all the new unsorted polylines it is time to sort them
8231 // so they be all contiguous
8232 sort_polylines_helper(unsorted_outer_polyline_pt, sorted_outer_curves_pt);
8233
8234 } // if (nunsorted_outer_polyline > 0)
8235
8236 // *********************************************************************
8237 // Step (2.2) Get the internal closed boundaries and check if it is
8238 // necessary to use a new representation (for example when the boundary
8239 // was splitted in the distribution process)
8240 // *********************************************************************
8241
8242 // Storage for new created polylines, non sorted
8243 Vector<TriangleMeshPolyLine*> unsorted_internal_closed_polyline_pt;
8244
8245 // Storing for the polylines on the boundaries
8246 // The first index is for a set of connected polylines
8247 // The second index is for a polyline on a set of connected polylines
8248 Vector<Vector<TriangleMeshPolyLine*>> sorted_internal_closed_curves_pt;
8249
8250 // Copy the internal closed boundaries to the vector of polylines
8251 const unsigned ninternal_closed = this->Internal_polygon_pt.size();
8252 for (unsigned i = 0; i < ninternal_closed; i++)
8253 {
8254 const unsigned npolylines = this->Internal_polygon_pt[i]->npolyline();
8255 for (unsigned p = 0; p < npolylines; p++)
8256 {
8257 // Pointer to the current polyline
8258 TriangleMeshPolyLine* tmp_polyline_pt =
8259 this->Internal_polygon_pt[i]->polyline_pt(p);
8260 const unsigned nvertex = tmp_polyline_pt->nvertex();
8261 if (nvertex > 0)
8262 {
8263 // Get the boundary id of the polyline and check if that boundary
8264 // needs a new representation (for example when the boundary was
8265 // splitted in the distribution process)
8266 const unsigned bound_id = tmp_polyline_pt->boundary_id();
8267 if (!boundary_was_splitted(bound_id))
8268 {
8269 unsorted_internal_closed_polyline_pt.push_back(tmp_polyline_pt);
8270 } // if (!boundary_was_splitted(bound_id))
8271 else
8272 {
8273 // Get the polylines that will represent this boundary
8274 Vector<TriangleMeshPolyLine*> tmp_vector_polylines =
8275 boundary_subpolylines(bound_id);
8276 const unsigned nsub_poly = tmp_vector_polylines.size();
8277#ifdef PARANOID
8278 if (nsub_poly <= 1)
8279 {
8280 std::ostringstream error_message;
8281 error_message << "The boundary (" << bound_id
8282 << ") was marked to be splitted but\n"
8283 << "there are only (" << nsub_poly
8284 << ") polylines to represent it.\n";
8285 throw OomphLibError(error_message.str(),
8286 OOMPH_CURRENT_FUNCTION,
8287 OOMPH_EXCEPTION_LOCATION);
8288 }
8289#endif
8290 // Add the new representation of the polylines (sub-polylines)
8291 // to represent this boundary
8292 for (unsigned isub = 0; isub < nsub_poly; isub++)
8293 {
8294 unsorted_internal_closed_polyline_pt.push_back(
8295 tmp_vector_polylines[isub]);
8296#ifdef PARANOID
8297 const unsigned nsvertex = tmp_vector_polylines[isub]->nvertex();
8298 if (nsvertex == 0)
8299 {
8300 std::ostringstream error_message;
8301 error_message
8302 << "The current chunk (" << isub << ") of the polyline with\n"
8303 << "boundary id (" << bound_id << ") has no vertices\n";
8304 throw OomphLibError(error_message.str(),
8305 OOMPH_CURRENT_FUNCTION,
8306 OOMPH_EXCEPTION_LOCATION);
8307 } // if (nsvertex == 0)
8308#endif // #ifdef PARANOID
8309 } // for (isub < nsub_poly)
8310 } // else if (!boundary_was_splitted(bound_id))
8311 } // if (nvertex > 0)
8312 } // for (p < npolylines)
8313 } // for (i < ninternal_closed)
8314
8315 const unsigned nunsorted_internal_closed_polyline =
8316 unsorted_internal_closed_polyline_pt.size();
8317
8318 if (nunsorted_internal_closed_polyline > 0)
8319 {
8320 // Now that we have all the new unsorted polylines it is time to sort them
8321 // so they be all contiguous
8322 sort_polylines_helper(unsorted_internal_closed_polyline_pt,
8323 sorted_internal_closed_curves_pt);
8324 }
8325
8326 // *********************************************************************
8327 // Step (2.3) Get the internal open boundaries and check if it is
8328 // necessary to use a new representation (for example when the boundary
8329 // was splitted in the distribution process)
8330 // *********************************************************************
8331
8332 // Storage for new created polylines, non sorted
8333 Vector<TriangleMeshPolyLine*> unsorted_internal_open_polyline_pt;
8334
8335 // Storing for the polylines on the boundaries
8336 // The first index is for a set of connected polylines
8337 // The second index is for a polyline on a set of connected polylines
8338 Vector<Vector<TriangleMeshPolyLine*>> sorted_internal_open_curves_pt;
8339
8340 // Copy the internal open boundaries to the vector of polylines
8341 const unsigned ninternal_open = this->Internal_open_curve_pt.size();
8342 for (unsigned i = 0; i < ninternal_open; i++)
8343 {
8344 const unsigned ncurve_section =
8345 this->Internal_open_curve_pt[i]->ncurve_section();
8346 for (unsigned p = 0; p < ncurve_section; p++)
8347 {
8348 // Pointer to the current polyline
8349 TriangleMeshPolyLine* tmp_polyline_pt =
8350 this->Internal_open_curve_pt[i]->polyline_pt(p);
8351 const unsigned nvertex = tmp_polyline_pt->nvertex();
8352 if (nvertex > 0)
8353 {
8354 // Get the boundary id of the polyline and check if that boundary
8355 // needs a new representation (for example when the boundary was
8356 // splitted in the distribution process)
8357 const unsigned bound_id = tmp_polyline_pt->boundary_id();
8358 if (!boundary_was_splitted(bound_id))
8359 {
8360 // Only include as internal boundaries those not marked as
8361 // shared boundaries
8362 if (!boundary_marked_as_shared_boundary(bound_id, 0))
8363 {
8364 unsorted_internal_open_polyline_pt.push_back(tmp_polyline_pt);
8365 }
8366 } // if (!boundary_was_splitted(bound_id))
8367 else
8368 {
8369 // Get the polylines that will represent this boundary
8370 Vector<TriangleMeshPolyLine*> tmp_vector_polylines =
8371 boundary_subpolylines(bound_id);
8372 const unsigned nsub_poly = tmp_vector_polylines.size();
8373#ifdef PARANOID
8374 if (nsub_poly <= 1)
8375 {
8376 std::ostringstream error_message;
8377 error_message << "The boundary (" << bound_id
8378 << ") was marked to be splitted but\n"
8379 << "there are only (" << nsub_poly
8380 << ") polylines to represent it.\n";
8381 throw OomphLibError(error_message.str(),
8382 OOMPH_CURRENT_FUNCTION,
8383 OOMPH_EXCEPTION_LOCATION);
8384 }
8385#endif
8386 // Add the new representation of the polylines (sub-polylines)
8387 // to represent this boundary
8388 for (unsigned isub = 0; isub < nsub_poly; isub++)
8389 {
8390 // Only include as internal boundaries those not marked as
8391 // shared boundaries
8392 if (!boundary_marked_as_shared_boundary(bound_id, isub))
8393 {
8394 unsorted_internal_open_polyline_pt.push_back(
8395 tmp_vector_polylines[isub]);
8396 }
8397#ifdef PARANOID
8398 const unsigned nsvertex = tmp_vector_polylines[isub]->nvertex();
8399 if (nsvertex == 0)
8400 {
8401 std::ostringstream error_message;
8402 error_message
8403 << "The current chunk (" << isub << ") of the polyline with\n"
8404 << "boundary id (" << bound_id << ") has no vertices\n";
8405 throw OomphLibError(error_message.str(),
8406 OOMPH_CURRENT_FUNCTION,
8407 OOMPH_EXCEPTION_LOCATION);
8408 } // if (nsvertex == 0)
8409#endif // #ifdef PARANOID
8410 } // for (isub < nsub_poly)
8411 } // else if (!boundary_was_splitted(bound_id))
8412 } // if (nvertex > 0)
8413 } // for (p < npolylines)
8414 } // for (i < ninternal_open)
8415
8416 const unsigned nunsorted_internal_open_polyline =
8417 unsorted_internal_open_polyline_pt.size();
8418
8419 if (nunsorted_internal_open_polyline > 0)
8420 {
8421 // Now that we have all the new unsorted polylines it is time to sort them
8422 // so they be all contiguous
8423 sort_polylines_helper(unsorted_internal_open_polyline_pt,
8424 sorted_internal_open_curves_pt);
8425 }
8426
8427 // ********************************************************************
8428 // Step (2.4) Sort the polylines on the shared boundaries
8429 // ********************************************************************
8430
8431 // Storage for new created polylines, non sorted
8432 Vector<TriangleMeshPolyLine*> unsorted_shared_polyline_pt;
8433
8434 // Special storage for the shared polylines that will be also used
8435 // to connect with the internal boundaries
8436 Vector<TriangleMeshPolyLine*> unsorted_shared_to_internal_polyline_pt;
8437
8438 // Storing for the polylines on the shared boundaries
8439 // The first index is for a set of connected polylines
8440 // The second index is for a polyline on a set of connected polylines
8441 Vector<Vector<TriangleMeshPolyLine*>> sorted_shared_curves_pt;
8442
8443 // Copy the shared boudaries to the vector of polylines
8444 const unsigned ncurves = nshared_boundary_curves(my_rank);
8445 for (unsigned i = 0; i < ncurves; i++)
8446 {
8447 const unsigned npolylines = nshared_boundary_polyline(my_rank, i);
8448 for (unsigned p = 0; p < npolylines; p++)
8449 {
8450 const unsigned nvertex =
8451 shared_boundary_polyline_pt(my_rank, i, p)->nvertex();
8452 if (nvertex > 0)
8453 {
8454 TriangleMeshPolyLine* tmp_shared_poly_pt =
8455 shared_boundary_polyline_pt(my_rank, i, p);
8456
8457 // First check if there are shared boundaries overlapping
8458 // internal boundaries
8459 if (this->nshared_boundary_overlaps_internal_boundary() > 0)
8460 {
8461 // Get the boundary id of the shared polyline
8462 const unsigned shd_bnd_id = tmp_shared_poly_pt->boundary_id();
8463 // If the shared polyline is marked as internal boundary
8464 // then include it in the special storage to look for
8465 // connection with internal boundaries
8466 if (this->shared_boundary_overlaps_internal_boundary(shd_bnd_id))
8467 {
8468 unsorted_shared_to_internal_polyline_pt.push_back(
8469 tmp_shared_poly_pt);
8470 }
8471 }
8472 unsorted_shared_polyline_pt.push_back(tmp_shared_poly_pt);
8473 }
8474 }
8475 }
8476
8477 // Get the total number of shared polylines
8478 const unsigned nunsorted_shared_polyline =
8479 unsorted_shared_polyline_pt.size();
8480
8481 if (nunsorted_shared_polyline > 0)
8482 {
8483 // Now that we have all the new unsorted polylines it is time to
8484 // sort them so they be all contiguous
8485 sort_polylines_helper(unsorted_shared_polyline_pt,
8486 sorted_shared_curves_pt);
8487 }
8488
8489 // ********************************************************************
8490 // Step (3) Join the boundaries (shared, internal and outer to
8491 // create the new polygons)
8492 // ********************************************************************
8493
8494 // Create the set of curves that will be used to create the new polygons
8495 // Get the total number of curves
8496 const unsigned nouter_curves = sorted_outer_curves_pt.size();
8497 const unsigned ninternal_closed_curves =
8498 sorted_internal_closed_curves_pt.size();
8499 const unsigned nshared_curves = sorted_shared_curves_pt.size();
8500 const unsigned ntotal_curves =
8501 nouter_curves + ninternal_closed_curves + nshared_curves;
8502
8503 // Add all the polylines to a container
8504 unsigned counter = 0;
8505 Vector<Vector<TriangleMeshPolyLine*>> all_curves_pt(ntotal_curves);
8506
8507 // Add the shared curves first, this ensure the generation of
8508 // internal polygons defined by the shared boundaries
8509 for (unsigned i = 0; i < nshared_curves; i++, counter++)
8510 {
8511 all_curves_pt[counter] = sorted_shared_curves_pt[i];
8512 }
8513
8514 // Add the internal polygons (if any)
8515 for (unsigned i = 0; i < ninternal_closed_curves; i++, counter++)
8516 {
8517 all_curves_pt[counter] = sorted_internal_closed_curves_pt[i];
8518 }
8519
8520 // Add the outer polygons
8521 for (unsigned i = 0; i < nouter_curves; i++, counter++)
8522 {
8523 all_curves_pt[counter] = sorted_outer_curves_pt[i];
8524 }
8525
8526 // Create the temporary version of the domain by joining the new
8527 // polylines
8528 this->create_tmp_polygons_helper(all_curves_pt, polygons_pt);
8529 // Create the new open curves
8530 this->create_tmp_open_curves_helper(sorted_internal_open_curves_pt,
8531 unsorted_shared_to_internal_polyline_pt,
8532 open_curves_pt);
8533
8534 // ********************************************************************
8535 // Step (4) Create connections among the outer boundaries
8536 // (intersections with themselves)
8537 // ********************************************************************
8538
8539 // After creating the new boundaries representation (polylines)
8540 // establish the connections of the shared boundaries (with
8541 // themselves or with the original boundaries). This avoids the
8542 // multiple definition of vertices in the domain which cause
8543 // problems when calling Triangle
8544
8545 this->create_shared_polylines_connections();
8546
8547 // ------------------------------------------------------------------
8548 // Compute the new holes information. Those from the
8549 // extra_holes_coordinates container, and those from the original
8550 // closed boundaries. Add the holes created by the halo elements
8551 // adjacent to the shared boundaries
8552
8553 // The storage for the new holes, get those from the
8554 // extra_holes_coordinates container and those from the internal
8555 // closed boundaries that are defined as holes
8556 Vector<Vector<double>> new_holes_coordinates;
8557
8558 // Copy the holes (those defined by the original internal closed
8559 // boundaries and those in the extra holes container)
8560
8561 // The holes defined by the original internal closed boundaries
8562 const unsigned n_holes = this->Internal_polygon_pt.size();
8563 for (unsigned h = 0; h < n_holes; h++)
8564 {
8565 Vector<double> hole_coordinates =
8566 this->Internal_polygon_pt[h]->internal_point();
8567 // If the closed boundary is a hole, then copy its hole
8568 if (!hole_coordinates.empty())
8569 {
8570 new_holes_coordinates.push_back(hole_coordinates);
8571 }
8572 } // for (h < n_holes)
8573
8574 // Is this the first time we are going to copy the extra holes
8575 // coordinates
8576 if (First_time_compute_holes_left_by_halo_elements)
8577 {
8578 // The holes in the extra holes container
8579 const unsigned n_extra_holes = Extra_holes_coordinates.size();
8580 for (unsigned h = 0; h < n_extra_holes; h++)
8581 {
8582 Vector<double> hole_coordinates = Extra_holes_coordinates[h];
8583 new_holes_coordinates.push_back(hole_coordinates);
8584 } // for (h < n_extra_holes)
8585
8586 // Copy the extra holes coordinates
8587 Original_extra_holes_coordinates = Extra_holes_coordinates;
8588
8589 // Set the flag to false
8590 First_time_compute_holes_left_by_halo_elements = false;
8591
8592 } // if (First_time_compute_holes_left_by_halo_elements)
8593 else
8594 {
8595 // Not the first time, then only copy the original extra holes
8596 // coordinates
8597 const unsigned n_original_extra_holes =
8598 Original_extra_holes_coordinates.size();
8599 for (unsigned h = 0; h < n_original_extra_holes; h++)
8600 {
8601 Vector<double> hole_coordinates = Original_extra_holes_coordinates[h];
8602 new_holes_coordinates.push_back(hole_coordinates);
8603 } // for (h < n_original_extra_holes)
8604 }
8605
8606 // Add the holes created by the halo elements adjacent to the shared
8607 // boundaries
8608 compute_holes_left_by_halo_elements_helper(new_holes_coordinates);
8609
8610 // Update the holes information, only use the coordinate inside the
8611 // poylgons that define the new domain
8612 update_holes_information_helper(polygons_pt, new_holes_coordinates);
8613
8614 // tachidok Clear the storage by now
8615 // new_holes_coordinates.clear();
8616
8617 // Now copy the info. in the extra holes coordinates container
8618 Extra_holes_coordinates = new_holes_coordinates;
8619
8620 // Do not delete halo(ed) info., this will be "deleted"
8621 // automatically by not passing that information to the new adapted
8622 // mesh. Once the transfer of target areas is performed the halo(ed)
8623 // information is no longer required
8624 }
8625
8626 //======================================================================
8627 // Take the polylines from the shared boundaries and the boundaries
8628 // to create polygons
8629 //======================================================================
8630 template<class ELEMENT>
8633 Vector<TriangleMeshPolygon*>& polygons_pt)
8634 {
8635 // Each vector of polylines (curve) is already sorted, it means that
8636 // all the polylines on the vector polylines_pt[i] point to the same
8637 // direction
8638
8639 // --- Using this fact we should compare the first and last points from
8640 // these arrays of polylines (curves) and compare with the others
8641 // vectors of polylines (curves) end points
8642 // --- Once created a closed curve create a polygon
8643
8644 // The number of curves
8645 const unsigned ncurves = polylines_pt.size();
8646
8647 // The number of non sorted curves
8648 const unsigned nunsorted_curves = ncurves;
8649 // The number of sorted curves
8650 unsigned nsorted_curves = 0;
8651
8652 // Vector to know which ncurve is already done
8653 std::vector<bool> done_curve(ncurves);
8654
8655 do
8656 {
8657 // The list where to add the curves so that they be contiguous
8658 std::list<Vector<TriangleMeshPolyLine*>> list_building_polygon_pt;
8659#ifdef PARANOID
8660 // Flag to indicate that a root curve was found
8661 bool root_curve_found = false;
8662#endif
8663
8664 // The index for the root_curve (we use it in further iterations as the
8665 // starting index so we dont need to search in already done curves)
8666 unsigned root_curve_idx = 0;
8667
8668 // Get the root curve
8669 for (unsigned ic = 0; ic < ncurves; ic++)
8670 {
8671 if (!done_curve[ic])
8672 {
8673 root_curve_idx = ic;
8674 nsorted_curves++;
8675#ifdef PARANOID
8676 root_curve_found = true;
8677#endif
8678 done_curve[ic] = true;
8679 // ... break the loop
8680 break;
8681 }
8682 }
8683
8684#ifdef PARANOID
8685 if (!root_curve_found)
8686 {
8687 std::stringstream err;
8688 err << "The root curve to create a polygon from the shared and "
8689 << "original boundaries was not found!!!\n";
8690 throw OomphLibError(err.str(),
8691 "TriangleMesh::create_tmp_polygons_helper()",
8692 OOMPH_EXCEPTION_LOCATION);
8693 }
8694#endif
8695
8696 // Get the root curve
8697 Vector<TriangleMeshPolyLine*> root_curve_pt =
8698 polylines_pt[root_curve_idx];
8699
8700 // Add the root curve to the list
8701 list_building_polygon_pt.push_back(root_curve_pt);
8702
8703 // Get the initial and final vertices from the root curve
8704 Vector<double> root_curve_initial_vertex(2);
8705 Vector<double> root_curve_final_vertex(2);
8706
8707 // We need to get the number of polylines that compose the root curve
8708 const unsigned nroot_curve_polyline = root_curve_pt.size();
8709 // ... and now get the initial and final vertex
8710 root_curve_pt[0]->initial_vertex_coordinate(root_curve_initial_vertex);
8711 root_curve_pt[nroot_curve_polyline - 1]->final_vertex_coordinate(
8712 root_curve_final_vertex);
8713
8714 // First check if it already create a polygon
8715 double diff =
8716 ((root_curve_initial_vertex[0] - root_curve_final_vertex[0]) *
8717 (root_curve_initial_vertex[0] - root_curve_final_vertex[0])) +
8718 ((root_curve_initial_vertex[1] - root_curve_final_vertex[1]) *
8719 (root_curve_initial_vertex[1] - root_curve_final_vertex[1]));
8720 diff = sqrt(diff);
8722 {
8723 // The polyline already create a Polygon, then create it!!!
8724 // Create the curve section representation of the current root curve
8725 Vector<TriangleMeshCurveSection*> curve_section_pt(
8726 nroot_curve_polyline);
8727
8728 // Copy the polylines into its curve section representation
8729 for (unsigned i = 0; i < nroot_curve_polyline; i++)
8730 {
8731 curve_section_pt[i] = root_curve_pt[i];
8732 }
8733
8734 // ... and create the Polygon
8735 TriangleMeshPolygon* new_polygon_pt =
8736 new TriangleMeshPolygon(curve_section_pt);
8737
8738 // Mark the polygon for deletion (in the destructor)
8739 this->Free_polygon_pt.insert(new_polygon_pt);
8740
8741 // Add the polygon to the output polygons
8742 polygons_pt.push_back(new_polygon_pt);
8743 } // (diff < ToleranceForVertexMismatchInPolygons::Tolerable_error)
8744 // when the curve creates a Polygon by itself
8745 else
8746 {
8747 // Flag to continue iterating while curves be added to the left
8748 // or right of the list of curves
8749 bool added_curve = false;
8750#ifdef PARANOID
8751 // Flag to know if the "loop" finish because a polygon was
8752 // created or because no more curves can be added to the left or
8753 // right
8754 bool polygon_created = false;
8755#endif
8756 do
8757 {
8758 added_curve = false;
8759 // If the root curve does not create a closed polygon then add curves
8760 // to the left or right until the curves create a closed polygon
8761 for (unsigned ic = root_curve_idx + 1; ic < ncurves; ic++)
8762 {
8763 if (!done_curve[ic])
8764 {
8765 // Get the current curve
8766 Vector<TriangleMeshPolyLine*> current_curve_pt = polylines_pt[ic];
8767
8768 // We need to get the number of polylines that compose the
8769 // current curve
8770 const unsigned ncurrent_curve_polyline = current_curve_pt.size();
8771
8772 // ... and get the initial and final coordinates for the current
8773 // curve
8774 Vector<double> current_curve_initial_vertex(2);
8775 Vector<double> current_curve_final_vertex(2);
8776
8777 current_curve_pt[0]->initial_vertex_coordinate(
8778 current_curve_initial_vertex);
8779 current_curve_pt[ncurrent_curve_polyline - 1]
8780 ->final_vertex_coordinate(current_curve_final_vertex);
8781
8782 // ---------------------------------------------------------------
8783 // Start adding curves to the left or right
8784 // ---------------------------------------------------------------
8785 diff = ((current_curve_final_vertex[0] -
8786 root_curve_initial_vertex[0]) *
8787 (current_curve_final_vertex[0] -
8788 root_curve_initial_vertex[0])) +
8789 ((current_curve_final_vertex[1] -
8790 root_curve_initial_vertex[1]) *
8791 (current_curve_final_vertex[1] -
8792 root_curve_initial_vertex[1]));
8793 diff = sqrt(diff);
8794 // CURRENT curve to the LEFT of the ROOT curve
8796 {
8797 // Add the current curve to the left
8798 list_building_polygon_pt.push_front(current_curve_pt);
8799 // Mark the curve as done
8800 done_curve[ic] = true;
8801 // Update the initial vertex values
8802 root_curve_initial_vertex[0] = current_curve_initial_vertex[0];
8803 root_curve_initial_vertex[1] = current_curve_initial_vertex[1];
8804 // Increase the number of sorted curves
8805 nsorted_curves++;
8806 // Set the flag to indicate that a curve was added to the list
8807 added_curve = true;
8808 break;
8809 }
8810
8811 diff = ((current_curve_initial_vertex[0] -
8812 root_curve_initial_vertex[0]) *
8813 (current_curve_initial_vertex[0] -
8814 root_curve_initial_vertex[0])) +
8815 ((current_curve_initial_vertex[1] -
8816 root_curve_initial_vertex[1]) *
8817 (current_curve_initial_vertex[1] -
8818 root_curve_initial_vertex[1]));
8819 diff = sqrt(diff);
8820 // CURRENT curve to the LEFT of the ROOT curve but INVERTED
8822 {
8823 Vector<TriangleMeshPolyLine*> tmp_curve_pt(
8824 ncurrent_curve_polyline);
8825 // Reverse each polyline and back them up
8826 for (unsigned it = 0; it < ncurrent_curve_polyline; it++)
8827 {
8828 current_curve_pt[it]->reverse();
8829 tmp_curve_pt[it] = current_curve_pt[it];
8830 }
8831 // Now copy them back but in reverse order
8832 unsigned count = 0;
8833 for (int i = ncurrent_curve_polyline - 1; i >= 0; i--, count++)
8834 {
8835 current_curve_pt[count] = tmp_curve_pt[i];
8836 }
8837 // Add the current curve to the left
8838 list_building_polygon_pt.push_front(current_curve_pt);
8839 // Mark the curve as done
8840 done_curve[ic] = true;
8841 // Update the initial vertex values
8842 root_curve_initial_vertex[0] = current_curve_final_vertex[0];
8843 root_curve_initial_vertex[1] = current_curve_final_vertex[1];
8844 // Increase the number of sorted curves
8845 nsorted_curves++;
8846 // Set the flag to indicate that a curve was added to the list
8847 added_curve = true;
8848 break;
8849 }
8850
8851 diff = ((current_curve_initial_vertex[0] -
8852 root_curve_final_vertex[0]) *
8853 (current_curve_initial_vertex[0] -
8854 root_curve_final_vertex[0])) +
8855 ((current_curve_initial_vertex[1] -
8856 root_curve_final_vertex[1]) *
8857 (current_curve_initial_vertex[1] -
8858 root_curve_final_vertex[1]));
8859 diff = sqrt(diff);
8860 // CURRENT curve to the RIGHT of the ROOT curve
8862 {
8863 // Add the current curve to the right
8864 list_building_polygon_pt.push_back(current_curve_pt);
8865 // Mark the curve as done
8866 done_curve[ic] = true;
8867 // Update the initial vertex values
8868 root_curve_final_vertex[0] = current_curve_final_vertex[0];
8869 root_curve_final_vertex[1] = current_curve_final_vertex[1];
8870 // Increase the number of sorted curves
8871 nsorted_curves++;
8872 // Set the flag to indicate that a curve was added to the list
8873 added_curve = true;
8874 break;
8875 }
8876
8877 diff =
8878 ((current_curve_final_vertex[0] - root_curve_final_vertex[0]) *
8879 (current_curve_final_vertex[0] - root_curve_final_vertex[0])) +
8880 ((current_curve_final_vertex[1] - root_curve_final_vertex[1]) *
8881 (current_curve_final_vertex[1] - root_curve_final_vertex[1]));
8882 diff = sqrt(diff);
8883 // CURRENT curve to the RIGHT of the ROOT curve but INVERTED
8885 {
8886 Vector<TriangleMeshPolyLine*> tmp_curve_pt(
8887 ncurrent_curve_polyline);
8888 // Reverse each polyline and back them up
8889 for (unsigned it = 0; it < ncurrent_curve_polyline; it++)
8890 {
8891 current_curve_pt[it]->reverse();
8892 tmp_curve_pt[it] = current_curve_pt[it];
8893 }
8894 // Now copy them back but in reverse order
8895 unsigned count = 0;
8896 for (int i = ncurrent_curve_polyline - 1; i >= 0; i--, count++)
8897 {
8898 current_curve_pt[count] = tmp_curve_pt[i];
8899 }
8900 // Add the current curve to the right
8901 list_building_polygon_pt.push_back(current_curve_pt);
8902 // Mark the curve as done
8903 done_curve[ic] = true;
8904 // Update the initial vertex values
8905 root_curve_final_vertex[0] = current_curve_initial_vertex[0];
8906 root_curve_final_vertex[1] = current_curve_initial_vertex[1];
8907 // Increase the number of sorted curves
8908 nsorted_curves++;
8909 // Set the flag to indicate that a curve was added to the list
8910 added_curve = true;
8911 break;
8912 }
8913
8914 } // if (!done_curve[ic])
8915
8916 } // for (ic < ncurves)
8917
8918 // After adding a curve check if it is possible to create a polygon
8919 double diff =
8920 ((root_curve_initial_vertex[0] - root_curve_final_vertex[0]) *
8921 (root_curve_initial_vertex[0] - root_curve_final_vertex[0])) +
8922 ((root_curve_initial_vertex[1] - root_curve_final_vertex[1]) *
8923 (root_curve_initial_vertex[1] - root_curve_final_vertex[1]));
8924 diff = sqrt(diff);
8926 {
8927 // If the curves already create a Polygon then go out of the
8928 // loop and create the Polygon
8929 added_curve = false;
8930#ifdef PARANOID
8931 // Set the flag to indicate that a Polygon has been created
8932 polygon_created = true;
8933#endif
8934 } // (diff <
8935 // ToleranceForVertexMismatchInPolygons::Tolerable_error)
8936 // when the curve creates a Polygon by itself
8937
8938 } while (added_curve);
8939
8940#ifdef PARANOID
8941 if (!polygon_created)
8942 {
8943 std::stringstream error_message;
8944 error_message
8945 << "It was no possible to create a TriangleMeshPolygon with "
8946 << "the input set of curves\n"
8947 << "These are the initial and final vertices in the current "
8948 << "sorted list of\nTriangleMeshPolyLines\n\n";
8949 Vector<double> init_vertex(2);
8950 Vector<double> final_vertex(2);
8951 unsigned icurve = 0;
8952 for (std::list<Vector<TriangleMeshPolyLine*>>::iterator it =
8953 list_building_polygon_pt.begin();
8954 it != list_building_polygon_pt.end();
8955 it++, icurve++)
8956 {
8957 const unsigned ncurrent_curve_polyline = (*it).size();
8958 error_message << "TriangleMeshCurve #" << icurve << "\n"
8959 << "-----------------------------------\n";
8960 for (unsigned ip = 0; ip < ncurrent_curve_polyline; ip++)
8961 {
8962 Vector<double> init_vertex(2);
8963 Vector<double> final_vertex(2);
8964 (*it)[ip]->initial_vertex_coordinate(init_vertex);
8965 (*it)[ip]->final_vertex_coordinate(final_vertex);
8966 error_message << "TriangleMeshPolyLine #" << ip << "\n"
8967 << "Initial vertex: (" << init_vertex[0] << ","
8968 << init_vertex[1] << ")\n"
8969 << "Final vertex: (" << final_vertex[0] << ","
8970 << final_vertex[1] << ")\n";
8971 } // for (ip < ncurrent_curve_polyline)
8972 } // for (it != list_building_polygon_pt.end())
8973
8974 throw OomphLibError(error_message.str(),
8975 "TriangleMesh::create_tmp_polygons_helper()",
8976 OOMPH_EXCEPTION_LOCATION);
8977
8978 } // if (!polygon_created)
8979#endif
8980
8981 // Create the polygon after joining the curves
8982 unsigned ntotal_polylines = 0;
8983 // Get the total number of polylines
8984 for (std::list<Vector<TriangleMeshPolyLine*>>::iterator it =
8985 list_building_polygon_pt.begin();
8986 it != list_building_polygon_pt.end();
8987 it++)
8988 {
8989 ntotal_polylines += (*it).size();
8990 }
8991
8992 // Create the curve section representation of the curves on the list
8993 Vector<TriangleMeshCurveSection*> curve_section_pt(ntotal_polylines);
8994
8995 // Copy the polylines into its curve section representation
8996 unsigned counter = 0;
8997 for (std::list<Vector<TriangleMeshPolyLine*>>::iterator it =
8998 list_building_polygon_pt.begin();
8999 it != list_building_polygon_pt.end();
9000 it++)
9001 {
9002 const unsigned ncurrent_curve_polyline = (*it).size();
9003 for (unsigned ip = 0; ip < ncurrent_curve_polyline; ip++, counter++)
9004 {
9005 curve_section_pt[counter] = (*it)[ip];
9006 } // for (ip < ncurrent_curve_polyline)
9007 } // Loop over the list of polylines
9008
9009 // ... and create the Polygon
9010 TriangleMeshPolygon* new_polygon_pt =
9011 new TriangleMeshPolygon(curve_section_pt);
9012
9013 // Mark the polygon for deletion (in the destructor)
9014 this->Free_polygon_pt.insert(new_polygon_pt);
9015
9016 // Add the polygon to the output polygons
9017 polygons_pt.push_back(new_polygon_pt);
9018
9019 } // else
9020 // (diff < ToleranceForVertexMismatchInPolygons::Tolerable_error)
9021
9022 } while (nsorted_curves < nunsorted_curves);
9023 }
9024
9025 //======================================================================
9026 // Take the polylines from the original open curves and created
9027 // new temporaly representations of open curves with the bits of
9028 // original curves not overlapped by shared boundaries
9029 //======================================================================
9030 template<class ELEMENT>
9032 Vector<Vector<TriangleMeshPolyLine*>>& sorted_open_curves_pt,
9033 Vector<TriangleMeshPolyLine*>& unsorted_shared_to_internal_poly_pt,
9034 Vector<TriangleMeshOpenCurve*>& open_curves_pt)
9035 {
9036 // Here search for the connections of the open curves remaining as
9037 // open curves with the shared boundaries markes as internal
9038 const unsigned ninternal_open_curves = sorted_open_curves_pt.size();
9039
9040 // Once identified the connections created with the new internal
9041 // boundaries representations add them to the open curves container
9042 for (unsigned i = 0; i < ninternal_open_curves; i++)
9043 {
9044 // Create the curve section representation of the polylines
9045 const unsigned npoly = sorted_open_curves_pt[i].size();
9046 Vector<TriangleMeshCurveSection*> tmp_curve_section(npoly);
9047 for (unsigned j = 0; j < npoly; j++)
9048 {
9049 tmp_curve_section[j] = sorted_open_curves_pt[i][j];
9050 }
9051 // ... and create the Open Curve
9052 TriangleMeshOpenCurve* new_open_curve_pt =
9053 new TriangleMeshOpenCurve(tmp_curve_section);
9054
9055 // Mark the open curve for deletion (in the destructor)
9056 this->Free_open_curve_pt.insert(new_open_curve_pt);
9057
9058 // Add the open curve to the output open curves
9059 open_curves_pt.push_back(new_open_curve_pt);
9060
9061 } // (i < ninternal_open_curves)
9062 }
9063
9064 //======================================================================
9065 // Check for any possible connections that the array of sorted
9066 // nodes have with original boundary nodes, previous shared polyline
9067 // nodes or with itself polyline nodes. In case that there is a
9068 // connection, get the boundary id to which connects
9069 //======================================================================
9070 template<class ELEMENT>
9072 std::set<FiniteElement*>& element_in_processor_pt,
9073 const int& root_edge_bnd_id,
9074 std::map<std::pair<Node*, Node*>, bool>& overlapped_face,
9075 std::map<unsigned, std::map<Node*, bool>>&
9076 node_on_bnd_not_overlapped_by_shd_bnd,
9077 std::list<Node*>& current_polyline_nodes,
9078 std::map<unsigned, std::list<Node*>>& shared_bnd_id_to_sorted_list_node_pt,
9079 const unsigned& node_degree,
9080 Node*& new_node_pt,
9081 const bool called_from_load_balance)
9082 {
9083 // Initialize the flag to return
9084 int flag_to_return = -1;
9085
9086 // --------------------------------------------------------------------
9087 // First try to find a connection with any original boundary (keep
9088 // in mind the case when internal boundaries may be overlapped by
9089 // shared boundaries)
9090 // --------------------------------------------------------------------
9091
9092 // Check if the shared boundary is overlapping an internal boundary
9093 bool overlapping_internal_boundary = false;
9094 // The boundary id overlapped by the current shared boundary
9095 unsigned internal_overlaping_bnd_id = 0;
9096 if (root_edge_bnd_id != -1)
9097 {
9098 // Set the flat to true
9099 overlapping_internal_boundary = true;
9100 // Set the bnd id of the overlapped internal boundary
9101 internal_overlaping_bnd_id = static_cast<unsigned>(root_edge_bnd_id);
9102 } // if (root_edge_bnd_id != -1)
9103
9104 // ---------------------------------------------------------------
9105 // Check if the connection is with an original boundary by checking
9106 // if the new node is a boundary node, and it lives in an element
9107 // that is part of the domain
9108 // ---------------------------------------------------------------
9109 if (new_node_pt->is_on_boundary())
9110 {
9111 // Flag to indicate if the node lives in a non overlapped boundary
9112 bool is_node_living_in_non_overlapped_boundary = false;
9113
9114 // If the node is a boundary node then check in which boundary it
9115 // is
9116 const unsigned noriginal_bnd = this->initial_shared_boundary_id();
9117 for (unsigned bb = 0; bb < noriginal_bnd; bb++)
9118 {
9119 // If the shared boundary overlaps an internal boundary it will
9120 // be indicated by (root_edge_bnd_id != -1), the original
9121 // internal boundary that overlaps is given by the
9122 // root_edge_bnd_id value. We skip that original internal
9123 // boundary because the new node will be obviously ON the
9124 // internal boundary
9125 if (overlapping_internal_boundary)
9126 {
9127 // Is the node on boundary bb?
9128 if (new_node_pt->is_on_boundary(bb))
9129 {
9130 // If overlaping then check that the boundary is different
9131 // from the one that is being overlapped, or if overlapped
9132 // then check that the node is on an edge on the bb
9133 // boundary not overlapped by a shared boundary
9134 const bool on_bnd_edge_not_overlapped_by_shd_bnd =
9135 node_on_bnd_not_overlapped_by_shd_bnd[bb][new_node_pt];
9136 if (bb != internal_overlaping_bnd_id ||
9137 ((bb == internal_overlaping_bnd_id) &&
9138 (on_bnd_edge_not_overlapped_by_shd_bnd)))
9139 {
9140 // Is the node living in a non overlapped boundary
9141 if (bb != internal_overlaping_bnd_id)
9142 {
9143 is_node_living_in_non_overlapped_boundary = true;
9144 }
9145
9146 // Now we need to check that the node lies on a boundary
9147 // that still exist (the elements associated to the
9148 // boundary may have been removed at the mesh distribution
9149 // stage). The node may be still marked as a boundary node
9150 // but the boundary may not have elements associated.
9151
9152 // Get the number of elements in the boundary
9153 const unsigned n_bound_ele = this->nboundary_element(bb);
9154 if (n_bound_ele > 0)
9155 {
9156 // Check that node lies on a nonhalo element, those are
9157 // the elements used to update the domain representation
9158 for (unsigned e = 0; e < n_bound_ele; e++)
9159 {
9160 // Get the boundary bulk element
9161 FiniteElement* bulk_ele_pt = this->boundary_element_pt(bb, e);
9162 // Check if the element will be retained, it means it
9163 // is a nonhalo element
9164 std::set<FiniteElement*>::iterator it =
9165 element_in_processor_pt.find(bulk_ele_pt);
9166 // If found then check if the node live in the element
9167 if (it != element_in_processor_pt.end())
9168 {
9169 // Found the node in the nonhalo face element
9170 bool found_node = false;
9171 // Get the face index
9172 int face_index = this->face_index_at_boundary(bb, e);
9173 // Create the face element
9174 FiniteElement* face_ele_pt =
9175 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
9176 // Get the number of nodes in the face element
9177 const unsigned n_node_face = face_ele_pt->nnode();
9178 // Get the first and last node of the face element
9179 Node* first_node_pt = face_ele_pt->node_pt(0);
9180 Node* last_node_pt = face_ele_pt->node_pt(n_node_face - 1);
9181 // Create the edge with the pair of nodes
9182 std::pair<Node*, Node*> tmp_edge =
9183 std::make_pair(first_node_pt, last_node_pt);
9184 // Check if the face element edge is overlapped by a
9185 // shared boundary
9186 // Is the face not overlapped?
9187 if (!overlapped_face[tmp_edge])
9188 {
9189 // Look for the node in the current face element
9190 for (unsigned n = 0; n < n_node_face; n++)
9191 {
9192 // Check for every individual node
9193 if (face_ele_pt->node_pt(n) == new_node_pt)
9194 {
9195 found_node = true;
9196 break;
9197 } // if (face_ele_pt->node_pt(n) == new_node_pt)
9198 } // for (n < n_node_face)
9199 } // if (!overlapped_face[tmp_edge])
9200 // Free the memory of the face element
9201 delete face_ele_pt;
9202 if (found_node)
9203 {
9204 // return the first original boundary id found,
9205 // does not matter if the node lies on more than
9206 // one original boundary (with boundary
9207 // elements). This is the original boundary id
9208 // that will be used to create the connection
9209 flag_to_return = bb;
9210 return flag_to_return;
9211 } // if (found_node)
9212
9213 } // if (it!=element_in_processor_pt.end())
9214
9215 } // for (e < n_bound_ele)
9216
9217 } // if (n_bound_ele > 0)
9218
9219 } // if (bb != internal_overlaping_bnd_id ||
9220 // ((bb == internal_overlaping_bnd_id) &&
9221 // (on_bnd_edge_not_overlapped_by_shd_bnd)))
9222
9223 } // if (nod_pt->is_on_boundary(bb))
9224
9225 } // if (overlapping_internal_boundary)
9226 else
9227 {
9228 // Is the node on boundary bb?
9229 if (new_node_pt->is_on_boundary(bb))
9230 {
9231 // Now we need to check that the node lies on a boundary
9232 // that still exist (the elements associated to the boundary
9233 // may have been removed at the mesh distribution
9234 // stage). The node may be still marked as a boundary node
9235 // but the boundary may not have elements associated.
9236
9237 // Get the number of elements in the boundary
9238 const unsigned n_bound_ele = this->nboundary_element(bb);
9239 if (n_bound_ele > 0)
9240 {
9241 // Check that node lies on a nonhalo element, those are
9242 // the elements used to update the domain representation
9243 for (unsigned e = 0; e < n_bound_ele; e++)
9244 {
9245 // Get the boundary bulk element
9246 FiniteElement* bulk_ele_pt = this->boundary_element_pt(bb, e);
9247 // Check if the element will be retained, it means it is
9248 // a nonhalo element
9249 std::set<FiniteElement*>::iterator it =
9250 element_in_processor_pt.find(bulk_ele_pt);
9251 // If found then check if the node live in the element
9252 if (it != element_in_processor_pt.end())
9253 {
9254 // Found the node in the nonhalo face element
9255 bool found_node = false;
9256 // Get the face index
9257 int face_index = this->face_index_at_boundary(bb, e);
9258 // Create the face element
9259 FiniteElement* face_ele_pt =
9260 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
9261 // Get the number of nodes in the face element
9262 const unsigned n_node_face = face_ele_pt->nnode();
9263 // Get the first and last node of the face element
9264 Node* first_node_pt = face_ele_pt->node_pt(0);
9265 Node* last_node_pt = face_ele_pt->node_pt(n_node_face - 1);
9266 // Create the edge with the pair of nodes
9267 std::pair<Node*, Node*> tmp_edge =
9268 std::make_pair(first_node_pt, last_node_pt);
9269 // Check if the face element edge is overlapped by a
9270 // shared boundary
9271 // Is the face not overlapped?
9272 if (!overlapped_face[tmp_edge])
9273 {
9274 // Look for the node in the current face element
9275 for (unsigned n = 0; n < n_node_face; n++)
9276 {
9277 // Check for every individual node
9278 if (face_ele_pt->node_pt(n) == new_node_pt)
9279 {
9280 found_node = true;
9281 break;
9282 } // if (face_ele_pt->node_pt(n) == new_node_pt)
9283 } // for (n < n_node_face)
9284 } // if (!overlapped_face[tmp_edge])
9285 // Free the memory of the face element
9286 delete face_ele_pt;
9287 if (found_node)
9288 {
9289 // return the first original boundary id found, does
9290 // not matter if the node lies on more than one
9291 // original boundary (with boundary elements). This
9292 // is the original boundary id that will be used to
9293 // create the connection
9294 flag_to_return = bb;
9295 return flag_to_return;
9296 } // if (found_node)
9297
9298 } // if (it!=element_in_processor_pt.end())
9299
9300 } // for (e < n_bound_ele)
9301
9302 } // if (n_bound_ele > 0)
9303
9304 } // if (nod_pt->is_on_boundary(bb))
9305 } // else if (overlapping_internal_boundary)
9306 } // for (bb < noriginal_bnd)
9307
9308 // We will only reach this stage when the node was found to be
9309 // connected to an original boundary but the element(s) on that
9310 // boundary where the node should live are not part of the domain.
9311 // Think in a corner of a triangle which touches the boundary
9312 // which elements will not be part of the domain
9313
9314 // We need to break the currently forming polyline
9315 // flag_to_return = -3;
9316
9317 // We need to break the currently forming polyline if and only if
9318 // the boundary(ies) in which the node is living is(are) not an
9319 // overlapped boundary
9320 if (!overlapping_internal_boundary)
9321 {
9322 // If the boundary(ies) in which the node is living is(are) an
9323 // overlapped boundary then break the break the formation of the
9324 // polyline
9325 flag_to_return = -3;
9326 }
9327 else
9328 {
9329 // The boundary is overlapped, if the node lives in a non
9330 // overlapped boundary then we can break the formation of the
9331 // polyline
9332 if (is_node_living_in_non_overlapped_boundary)
9333 {
9334 flag_to_return = -3;
9335 } // if (is_node_living_in_non_overlapped_boundar)y
9336
9337 } // if (!overlapping_internal_boundary)
9338
9339 } // if (new_node_pt->is_on_boundary())
9340
9341 // Return inmediately if the connection is with an original boundary
9342 // whose elements are still part of the domain
9343 if (flag_to_return >= 0)
9344 {
9345 return flag_to_return;
9346 }
9347
9348 // ----------------------------------------------------------------------
9349 // Secondly, if there is not a connection with any original
9350 // boundary, or if there is connection but with an original boundary
9351 // whose elements are not part of the domain, then check for
9352 // connections with previously created shared polylines
9353 // ----------------------------------------------------------------------
9354 // Store all the previous shared polylines to which the current
9355 // found is found to be connected
9356 Vector<unsigned> candidate_shared_bnd_to_connect;
9357 // Check for all the previous polylines except the current one
9358 for (std::map<unsigned, std::list<Node*>>::iterator it =
9359 shared_bnd_id_to_sorted_list_node_pt.begin();
9360 it != shared_bnd_id_to_sorted_list_node_pt.end();
9361 it++)
9362 {
9363 // Get the boundary id of the list of nodes that created the
9364 // polyline (the shared boundary id associated with the list of
9365 // nodes)
9366 const unsigned i_bnd_id = (*it).first;
9367 // Get an iterator pointer to the list of nodes of the shared
9368 // polyline
9369 std::list<Node*>::iterator it_list = (*it).second.begin();
9370 // Get the total number of nodes associated to the boundary
9371 const unsigned n_nodes = (*it).second.size();
9372 // Search for connections in the list of nodes
9373 for (unsigned i = 0; i < n_nodes; i++, it_list++)
9374 {
9375 // Is the node already part of any other shared boundary
9376 if ((*it_list) == new_node_pt)
9377 {
9378 // Include the i-th boundary id in the list of candidate
9379 // shared boundaries to connect
9380 candidate_shared_bnd_to_connect.push_back(i_bnd_id);
9381 // Break the look with the i-th shared boundary, check with
9382 // the others shared boundaries
9383 break;
9384 } // if ((*it_list) == new_node_pt)
9385
9386 } // for (i < nnodes)
9387
9388 } // Loop over the shared boundaries and associated nodes
9389
9390 // Get the number of candidate shared boundaries to connect
9391 const unsigned n_candidate_shared_bnd_to_connect =
9392 candidate_shared_bnd_to_connect.size();
9393
9394 // Is there a connection with any previous shared polyline
9395 if (n_candidate_shared_bnd_to_connect > 0)
9396 {
9397 // If called from load balance we do not need to check if the
9398 // shared boundary is part of the processor since it certanily is,
9399 // only the shared boundaries that are pare of the processor are
9400 // used to created connection when creating the new shared
9401 // boundaries in the load balance rutine
9402 if (called_from_load_balance)
9403 {
9404 return candidate_shared_bnd_to_connect[0];
9405 }
9406
9407 // We need to ensure that the shared boundary to which we are
9408 // connecting is part of the current processor, if none of the
9409 // found shared bundaries is in the current processor then return
9410 // the flag for "connection with boundary not in the current
9411 // processor"
9412
9413 // Store the shared boundaries associated with the current processor
9414 Vector<unsigned> shared_bound_in_this_proc;
9415
9416 // Get the shared boundaries associated with the current processor
9417 shared_boundaries_in_this_processor(shared_bound_in_this_proc);
9418
9419 // If any of the candidate shared boundaries to connect is in the
9420 // current processor then return that shared boundary id
9421
9422 // The number of shared boundaries in the current processor
9423 const unsigned n_shared_bound_in_this_proc =
9424 shared_bound_in_this_proc.size();
9425
9426 // Loop over the candidate shared boundaries to connect
9427 for (unsigned i = 0; i < n_candidate_shared_bnd_to_connect; i++)
9428 {
9429 // Get the i-th candidate shared boundary to connect
9430 const unsigned i_candidate_shared_bnd =
9431 candidate_shared_bnd_to_connect[i];
9432
9433 // Loop over the shared boundaries in the current processor
9434 for (unsigned j = 0; j < n_shared_bound_in_this_proc; j++)
9435 {
9436 // Is the candidate boundary a shared boundary in this processor?
9437 if (i_candidate_shared_bnd == shared_bound_in_this_proc[j])
9438 {
9439 // Return the candidate shared boundary
9440 flag_to_return = i_candidate_shared_bnd;
9441 return flag_to_return;
9442 } // The candidate shared boundary is a boundary in the
9443 // current processor
9444
9445 } // for (j < n_shared_bound_in_this_proc)
9446
9447 } // for (i < n_candidate_shared_bnd_to_connect)
9448
9449 // If non of the candidate shared boundaries to connect is in the
9450 // current processor the mark that we need to stop the addition of
9451 // vertices at this side of the polyline
9452 flag_to_return = -3;
9453
9454 } // if (n_candidate_shared_bnd_to_connect > 0)
9455
9456 // Return inmediately if the connection is with a previuos shared
9457 // boundary
9458 if (flag_to_return >= 0)
9459 {
9460 return flag_to_return;
9461 }
9462
9463 // ------------------------------------------------------------------
9464 // Finally,check for connections with the same polyline (the shared
9465 // boundary that is being constructed). We are trying to avoid loops
9466 // or connections with the same shared boundary that is why this is
9467 // checked at the end
9468 // ------------------------------------------------------------------
9469 unsigned nrepeated = 0;
9470 for (std::list<Node*>::iterator it_list = current_polyline_nodes.begin();
9471 it_list != current_polyline_nodes.end();
9472 it_list++)
9473 {
9474 // There must be at least one repeated node (the one that we have
9475 // just added, and it should be the first or last node)
9476 if ((*it_list) == new_node_pt)
9477 {
9478 nrepeated++;
9479 }
9480 }
9481 // If the number of repeated nodes is greater than one then the
9482 // polyline has a connection with itself
9483 if (nrepeated > 1)
9484 {
9485 // Return the flag value to indicate connection with itself, we
9486 // can not return the boundary id of the current polyline since it
9487 // has not been already assigned
9488 flag_to_return = -2;
9489 }
9490
9491 // If there is no connection at all check the degree of the node, if
9492 // it is greater than 2 then return the flag to stop adding nodes
9493 // after this one
9494 if (node_degree > 2)
9495 {
9496 flag_to_return = -3;
9497 }
9498
9499 // Return the flag
9500 return flag_to_return;
9501 }
9502
9503 //======================================================================
9504 // Establish the connections of the polylines previously
9505 // marked as having connections. This connections were created in the
9506 // function TriangleMesh::create_polylines_from_halo_elements_helper().
9507 // In case of doing load balancing the connections were created by the
9508 // function RefineableTriangleMesh::create_new_shared_boundaries()
9509 // ======================================================================
9510 template<class ELEMENT>
9512 {
9513 // Get the rank of the current processor
9514 const unsigned my_rank = this->communicator_pt()->my_rank();
9515
9516 // Get the shared curves associated with this processor
9517 Vector<Vector<TriangleMeshPolyLine*>> shared_curves_pt =
9518 this->Shared_boundary_polyline_pt[my_rank];
9519
9520 // Loop through the shared boundaries on the current processor and
9521 // check if they are marked to create a connection
9522 const unsigned ncurves = shared_curves_pt.size();
9523 for (unsigned icurve = 0; icurve < ncurves; icurve++)
9524 {
9525 // Get the number of polylines in the current shared curve
9526 const unsigned npoly = shared_curves_pt[icurve].size();
9527 for (unsigned ipoly = 0; ipoly < npoly; ipoly++)
9528 {
9529 // Get the polyline representation of the shared boundary
9530 TriangleMeshPolyLine* shd_poly_pt = shared_curves_pt[icurve][ipoly];
9531
9532 // Get the boundary id of the current polyline
9533 const unsigned bound_id = shd_poly_pt->boundary_id();
9534
9535 // Is the left vertex connected
9536 const bool is_connected_to_the_left =
9537 shd_poly_pt->is_initial_vertex_connected();
9538
9539 // Is the right vertex connected
9540 const bool is_connected_to_the_right =
9541 shd_poly_pt->is_final_vertex_connected();
9542
9543 // -----------------------------------------------------------------
9544 // If there is a connection at one of the ends we need to
9545 // establish that connection
9546 if (is_connected_to_the_left || is_connected_to_the_right)
9547 {
9548 // Now get the new left and right vertices of the shared
9549 // polyline
9550 const unsigned n_vertex = shd_poly_pt->nvertex();
9551
9552 // Now get the polylines to where the current shared boundary is
9553 // connected and create the connections
9554
9555 // --------------------------------------------------------------
9556 // Connection to the left
9557 if (is_connected_to_the_left)
9558 {
9559 // Get the unsigned version of the bound id to connect to
9560 // the left
9561 const unsigned uconnection_to_the_left =
9562 shd_poly_pt->initial_vertex_connected_bnd_id();
9563
9564 // The pointer to the boundary to connect
9565 TriangleMeshPolyLine* poly_to_connect_pt = 0;
9566
9567 // Flag to indicate we are trying to connect to an split
9568 // boundary
9569 bool connecting_to_an_split_boundary = false;
9570
9571 // Flag to indicate we are trying to connecto to an internal
9572 // boundary that is overlaped by a shared boundary
9573 bool connecting_to_an_overlaped_boundary = false;
9574
9575 // Check if the connection is with itself
9576 if (uconnection_to_the_left == bound_id)
9577 {
9578 // Set the pointer to the polyline to connect
9579 poly_to_connect_pt = shd_poly_pt;
9580 }
9581 else
9582 {
9583 // Get the initial shared boundary ids
9584 const unsigned initial_shd_bnd_id = initial_shared_boundary_id();
9585 // Check if the boundary to connect is a shared polyline
9586 if (uconnection_to_the_left >= initial_shd_bnd_id)
9587 {
9588 // Get the polyline pointer representing the destination
9589 // boundary
9590 poly_to_connect_pt =
9591 boundary_polyline_pt(uconnection_to_the_left);
9592 } // if (uconnection_to_the_left >= initial_shd_bnd_id)
9593 else
9594 {
9595 // If we are going to connect to an original boundary
9596 // verify if the boundary was splitted during the
9597 // distribution process to consider all the chunks
9598 // (sub-polylines) of the boundary
9599 if (boundary_was_splitted(uconnection_to_the_left))
9600 {
9601 connecting_to_an_split_boundary = true;
9602 } // if (boundary_was_splitted(uconnection_to_the_left))
9603
9604 // If we are going to connect to an original boundary
9605 // verify if the boundary, or any of its chunks is
9606 // marked to be overlapped by a shared boundary, if that
9607 // is the case we first check for connections in the
9608 // shared boundary that overlaps the internal boundary,
9609 // or the chunks, and then check for connections in the
9610 // original boundary
9611 if (connecting_to_an_split_boundary)
9612 {
9613 // Get the number of chucks that represent the
9614 // destination boundary
9615 const unsigned n_sub_poly =
9616 nboundary_subpolylines(uconnection_to_the_left);
9617 // Now loop over the chunks of the destination
9618 // boundary and if any of them is marked to be
9619 // overlaped by a shared boundary then set the flag
9620 // and break the loop
9621 for (unsigned ii = 0; ii < n_sub_poly; ii++)
9622 {
9623 if (boundary_marked_as_shared_boundary(
9624 uconnection_to_the_left, ii))
9625 {
9626 // Mark the boundary as being overlaped by a
9627 // shared boundary
9628 connecting_to_an_overlaped_boundary = true;
9629 // Break, no need to look for more overlapings
9630 break;
9631 } // if (boundary_marked_as_shared_boundary(...))
9632 } // for (ii < n_sub_poly)
9633 } // if (connecting_to_an_split_boundary)
9634 else
9635 {
9636 // If not connecting to an split boundary then check
9637 // if the whole destination boundary is overlaped by
9638 // an internal boundary
9639 if (boundary_marked_as_shared_boundary(
9640 uconnection_to_the_left, 0))
9641 {
9642 // Mark the boundary as being overlaped by a shared
9643 // boundary
9644 connecting_to_an_overlaped_boundary = true;
9645 } // if (boundary_marked_as_shared_boundary(...))
9646 } // else if (connecting_to_an_split_boundary)
9647
9648 // If we are connecting neither to an split boundary nor
9649 // an overlaped boundary then get the pointer to the
9650 // original boundary
9651 if (!(connecting_to_an_split_boundary ||
9652 connecting_to_an_overlaped_boundary))
9653 {
9654 // Get the polyline pointer representing the
9655 // destination boundary
9656 poly_to_connect_pt =
9657 boundary_polyline_pt(uconnection_to_the_left);
9658 } // else if (NOT split, NOT overlaped)
9659 } // else if (uconnection_to_the_left >= initial_shd_bnd_id)
9660
9661 } // else if (uconnection_to_the_left == bound_id)
9662
9663#ifdef PARANOID
9664 // If we are not connecting to an original boundary
9665 // (connecting to the same shared boundary or to another
9666 // shared boundary) then the boundary should not be marked
9667 // as split
9668 if (!connecting_to_an_split_boundary)
9669 {
9670 if (boundary_was_splitted(uconnection_to_the_left))
9671 {
9672 std::stringstream error;
9673 error
9674 << "The current shared boundary (" << bound_id << ") was "
9675 << "marked to have a connection\nto the left with the "
9676 << "boundary (" << uconnection_to_the_left << ").\n"
9677 << "The problem is that the destination boundary (possibly\n"
9678 << "another shared boundary) is marked to be split\n"
9679 << "There should not be split shared boundaries\n\n";
9680 throw OomphLibError(
9681 error.str(),
9682 "TriangleMesh::create_shared_polylines_connections()",
9683 OOMPH_EXCEPTION_LOCATION);
9684 }
9685 } // if (!connecting_to_an_split_boundary)
9686#endif
9687
9688 // Now look for the vertex number on the destination
9689 // boundary(ies) -- in case that the boundary was split ---
9690
9691 // Do not check for same orientation, that was previously
9692 // worked by interchanging the connections boundaries (if
9693 // necessary)
9694
9695 // Get the left vertex in the shared boundary
9696 Vector<double> shd_bnd_left_vertex =
9697 shd_poly_pt->vertex_coordinate(0);
9698
9699 // If the boundary was not split then ...
9700 if (!connecting_to_an_split_boundary)
9701 {
9702 // ... check if the boundary is marked to be overlaped by
9703 // a shared boundary
9704 if (!connecting_to_an_overlaped_boundary)
9705 {
9706 // If that is not the case then we can safely look for
9707 // the vertex number on the destination boundar
9708 unsigned vertex_index = 0;
9709
9710 const bool found_vertex_index =
9711 get_connected_vertex_number_on_destination_polyline(
9712 poly_to_connect_pt, shd_bnd_left_vertex, vertex_index);
9713
9714 // If we could not find the vertex index to connect then
9715 // we are in trouble
9716 if (!found_vertex_index)
9717 {
9718 std::stringstream error;
9719 error
9720 << "The current shared boundary (" << bound_id << ") was "
9721 << "marked to have a connection\nto the left with the "
9722 << "boundary (" << uconnection_to_the_left << ").\n"
9723 << "The problem is that the left vertex of the current\n"
9724 << "shared boundary is not in the list of vertices of the\n"
9725 << "boundary to connect.\n\n"
9726 << "This is the left vertex of the current shared "
9727 "boundary\n"
9728 << "Left vertex: (" << shd_bnd_left_vertex[0] << ", "
9729 << shd_bnd_left_vertex[1] << ")\n\n"
9730 << "This is the list of vertices on the destination "
9731 << "boundary\n";
9732 const unsigned n_v = poly_to_connect_pt->nvertex();
9733 for (unsigned i = 0; i < n_v; i++)
9734 {
9735 Vector<double> cvertex =
9736 poly_to_connect_pt->vertex_coordinate(i);
9737 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
9738 << cvertex[1] << ")\n";
9739 }
9740 throw OomphLibError(
9741 error.str(),
9742 "TriangleMesh::create_shared_polylines_connections()",
9743 OOMPH_EXCEPTION_LOCATION);
9744 } // if (!found_vertex_index)
9745
9746 // Create the connection, the left vertex of the current
9747 // shared boundary is connected with the vertex_index-th
9748 // vertex on the destination boundary
9750 poly_to_connect_pt, vertex_index);
9751
9752 } // if (!connecting_to_an_overlaped_boundary)
9753 else
9754 {
9755 // If the boundary is marked to be overlaped by a shared
9756 // boundary then get that shared boundary and look for
9757 // the connection in that boundary
9758
9759 // The vertex where to store the index to connect
9760 unsigned vertex_index = 0;
9761 // A flag to indicate if the connection was found
9762 bool found_vertex_index = false;
9763
9764 // Get the shared boundary id that is overlaping the
9765 // internal boundary
9766 Vector<unsigned> dst_shd_bnd_ids;
9767 get_shared_boundaries_overlapping_internal_boundary(
9768 uconnection_to_the_left, dst_shd_bnd_ids);
9769
9770 // Get the number of shared polylines that were found to
9771 // overlap the internal boundary
9772 const unsigned n_shd_bnd_overlap_int_bnd =
9773 dst_shd_bnd_ids.size();
9774
9775 // Loop over the shared boundaries that overlap the
9776 // internal boundary and look for the vertex to connect
9777 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
9778 {
9779 // Get the shared polyline
9780 const unsigned new_connection_to_the_left =
9781 dst_shd_bnd_ids[ss];
9782
9783 // Get the shared polyline that is overlaping the
9784 // internal boundary
9785 poly_to_connect_pt =
9786 boundary_polyline_pt(new_connection_to_the_left);
9787
9788 if (poly_to_connect_pt != 0)
9789 {
9790 // Look for the vertex number in the destination
9791 // shared polyline
9792 found_vertex_index =
9793 get_connected_vertex_number_on_destination_polyline(
9794 poly_to_connect_pt, shd_bnd_left_vertex, vertex_index);
9795 } // if (poly_to_connect_pt!=0)
9796
9797 // If we have found the vertex to connect then
9798 // break the loop
9799 if (found_vertex_index)
9800 {
9801 break;
9802 } // if (found_vertex_index)
9803
9804 } // for (ss < n_shd_bnd_overlaping_int_bnd)
9805
9806#ifdef PARANOID
9807 // If we could not find the vertex index to connect then
9808 // we are in trouble
9809 if (!found_vertex_index)
9810 {
9811 std::stringstream error;
9812 error
9813 << "The current shared boundary (" << bound_id << ") was "
9814 << "marked to have a connection\nto the left with the "
9815 << "boundary (" << uconnection_to_the_left << ").\n"
9816 << "This last boundary is marked to be overlaped by "
9817 << "shared boundaries\n"
9818 << "The problem is that the left vertex of the current\n"
9819 << "shared boundary is not in the list of vertices of the\n"
9820 << "boundary to connect.\n\n"
9821 << "This is the left vertex of the current shared "
9822 "boundary\n"
9823 << "Left vertex: (" << shd_bnd_left_vertex[0] << ", "
9824 << shd_bnd_left_vertex[1] << ")\n\n"
9825 << "This is the list of vertices on the destination "
9826 << "boundary\n";
9827 Vector<unsigned> dst_shd_bnd_ids;
9828 get_shared_boundaries_overlapping_internal_boundary(
9829 uconnection_to_the_left, dst_shd_bnd_ids);
9830 const unsigned n_shd_bnd_overlap_int_bnd =
9831 dst_shd_bnd_ids.size();
9832 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
9833 {
9834 const unsigned new_connection_to_the_left =
9835 dst_shd_bnd_ids[ss];
9836 poly_to_connect_pt =
9837 boundary_polyline_pt(new_connection_to_the_left);
9838 if (poly_to_connect_pt != 0)
9839 {
9840 const unsigned shd_bnd_id_overlap =
9841 poly_to_connect_pt->boundary_id();
9842 error << "Shared boundary id(" << shd_bnd_id_overlap
9843 << ")\n";
9844 const unsigned n_v = poly_to_connect_pt->nvertex();
9845 for (unsigned i = 0; i < n_v; i++)
9846 {
9847 Vector<double> cvertex =
9848 poly_to_connect_pt->vertex_coordinate(i);
9849 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
9850 << cvertex[1] << ")\n";
9851 }
9852 } // if (poly_to_connect_pt != 0)
9853 } // for (ss < n_shd_bnd_overlap_int_bnd)
9854
9855 throw OomphLibError(
9856 error.str(),
9857 "TriangleMesh::create_shared_polylines_connections()",
9858 OOMPH_EXCEPTION_LOCATION);
9859
9860 } // if (!found_vertex_index)
9861#endif
9862
9863 // Create the connection, the left vertex of the current
9864 // shared boundary is connected with the vertex_index-th
9865 // vertex on the destination boundary
9867 poly_to_connect_pt, vertex_index);
9868
9869 } // else if (!connecting_to_an_overlaped_boundary)
9870
9871 } // if (!connecting_to_an_split_boundary)
9872 else
9873 {
9874 // If the boundary was split then we need to look for the
9875 // vertex in the sub-polylines
9876
9877 // Get the sub-polylines vector
9878 Vector<TriangleMeshPolyLine*> tmp_vector_subpolylines =
9879 boundary_subpolylines(uconnection_to_the_left);
9880
9881 // Get the number of sub-polylines
9882 const unsigned nsub_poly = tmp_vector_subpolylines.size();
9883#ifdef PARANOID
9884 if (nsub_poly <= 1)
9885 {
9886 std::ostringstream error_message;
9887 error_message
9888 << "The boundary (" << uconnection_to_the_left << ") was "
9889 << "marked to be splitted but\n"
9890 << "there are only (" << nsub_poly << ") polylines to "
9891 << "represent it.\n";
9892 throw OomphLibError(
9893 error_message.str(),
9894 "TriangleMesh::create_shared_polylines_connections()",
9895 OOMPH_EXCEPTION_LOCATION);
9896 } // if (nsub_poly <= 1)
9897#endif
9898 // We need to check if the boundary is marked to be
9899 // overlaped by an internal boundary, if that is the case
9900 // we need to check for each indivual subpolyline, and for
9901 // those overlaped by a shared polyline look for the
9902 // vertex in the shared polyline representation instead of
9903 // the original subpolyline
9904
9905 // ... check if the boundary is marked to be overlaped by
9906 // a shared boundary
9907 if (!connecting_to_an_overlaped_boundary)
9908 {
9909 // We can work without checking the subpolylines
9910 // individually
9911
9912 // The vertex where to store the index to connect
9913 unsigned vertex_index = 0;
9914 // The subpoly number to connect
9915 unsigned sub_poly_to_connect = 0;
9916 // A flag to indicate if the connection was found
9917 bool found_vertex_index = false;
9918
9919 // Look for the vertex number to connect on each of the
9920 // subpolyines
9921 for (unsigned isub = 0; isub < nsub_poly; isub++)
9922 {
9923 // Assign the pointer to the sub-polyline
9924 poly_to_connect_pt = tmp_vector_subpolylines[isub];
9925 // Search for the vertex in the current sub-polyline
9926 found_vertex_index =
9927 get_connected_vertex_number_on_destination_polyline(
9928 poly_to_connect_pt, shd_bnd_left_vertex, vertex_index);
9929 // If we have found the vertex to connect then break the
9930 // loop
9931 if (found_vertex_index)
9932 {
9933 // But first save the subpoly number (chunk), that
9934 // will be used to perform the connection
9935 sub_poly_to_connect = isub;
9936 break;
9937 } // if (found_vertex_index)
9938 } // for (isub < nsub_poly)
9939
9940#ifdef PARANOID
9941 // If we could not find the vertex index to connect then
9942 // we are in trouble
9943 if (!found_vertex_index)
9944 {
9945 std::stringstream error;
9946 error
9947 << "The current shared boundary (" << bound_id << ") was "
9948 << "marked to have a connection\nto the left with the "
9949 << "boundary (" << uconnection_to_the_left << ").\n"
9950 << "The problem is that the left vertex of the current\n"
9951 << "shared boundary is not in the list of vertices of any\n"
9952 << "of the sub polylines that represent the boundary to\n"
9953 << "connect.\n\n"
9954 << "This is the left vertex of the current shared "
9955 "boundary\n"
9956 << "Left vertex: (" << shd_bnd_left_vertex[0] << ", "
9957 << shd_bnd_left_vertex[1] << ")\n\n"
9958 << "This is the list of vertices on the destination "
9959 << "boundary\n";
9960 for (unsigned p = 0; p < nsub_poly; p++)
9961 {
9962 error << "Subpolyline #(" << p << ")\n";
9963 poly_to_connect_pt = tmp_vector_subpolylines[p];
9964 const unsigned n_v = poly_to_connect_pt->nvertex();
9965 for (unsigned i = 0; i < n_v; i++)
9966 {
9967 Vector<double> cvertex =
9968 poly_to_connect_pt->vertex_coordinate(i);
9969 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
9970 << cvertex[1] << ")\n";
9971 }
9972 } // for (p < nsub_poly)
9973 throw OomphLibError(
9974 error.str(),
9975 "TriangleMesh::create_shared_polylines_connections()",
9976 OOMPH_EXCEPTION_LOCATION);
9977 } // if (!found_vertex_index)
9978#endif
9979
9980 // Create the connection, the left vertex of the current
9981 // shared boundary is connected with the vertex_index-th
9982 // vertex of sub_poly_to_connect-th subpolyline of the
9983 // destination boundary
9985 poly_to_connect_pt, vertex_index, sub_poly_to_connect);
9986
9987 } // if (!connecting_to_an_overlaped_boundary)
9988 else
9989 {
9990 // We first look on the shared boundaries that overlap
9991 // the internal boundaries and the look for the
9992 // sub-polylines that are not marked as being overlaped
9993 // by shared boundaries
9994
9995 // The vertex where to store the index to connect
9996 unsigned vertex_index = 0;
9997 // The subpoly number to connect
9998 unsigned sub_poly_to_connect = 0;
9999 // A flag to indicate if the connection was found
10000 bool found_vertex_index = false;
10001
10002 // Get the shared boundaries id that are overlaping the
10003 // internal boundary
10004 Vector<unsigned> dst_shd_bnd_ids;
10005 get_shared_boundaries_overlapping_internal_boundary(
10006 uconnection_to_the_left, dst_shd_bnd_ids);
10007
10008 // Get the number of shared polylines that were found to
10009 // overlap the internal boundary
10010 const unsigned n_shd_bnd_overlap_int_bnd =
10011 dst_shd_bnd_ids.size();
10012
10013 // Loop over the shared boundaries that overlap the
10014 // internal boundary and look for the vertex to connect
10015 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10016 {
10017 // Get the shared polyline
10018 const unsigned new_connection_to_the_left =
10019 dst_shd_bnd_ids[ss];
10020
10021 // Make sure that the destination polyline is not the
10022 // same as the current shared polyline
10023 if (bound_id != new_connection_to_the_left)
10024 {
10025 // Get the shared polyline that is overlaping the
10026 // internal boundary
10027 poly_to_connect_pt =
10028 boundary_polyline_pt(new_connection_to_the_left);
10029
10030 if (poly_to_connect_pt != 0)
10031 {
10032 // Look for the vertex number in the destination
10033 // shared polyline
10034 found_vertex_index =
10035 get_connected_vertex_number_on_destination_polyline(
10036 poly_to_connect_pt,
10037 shd_bnd_left_vertex,
10038 vertex_index);
10039 } // if (poly_to_connect_pt != 0)
10040
10041 // If we have found the vertex to connect then
10042 // break the loop
10043 if (found_vertex_index)
10044 {
10045 break;
10046 } // if (found_vertex_index)
10047
10048 } // if (bound_id != new_connection_to_the_left)
10049
10050 } // for (ss < n_shd_bnd_overlaping_int_bnd)
10051
10052 // If we have not yet found the vertex then look for it
10053 // in the sub-polylines that are not overlaped by shared
10054 // boundaries
10055 if (!found_vertex_index)
10056 {
10057 // Look for the vertex number to connect on each of
10058 // the subpolyines
10059 for (unsigned isub = 0; isub < nsub_poly; isub++)
10060 {
10061 // Only work with those sub-polylines that are not
10062 // overlaped by shared boundaries
10063 if (!boundary_marked_as_shared_boundary(
10064 uconnection_to_the_left, isub))
10065 {
10066 // Assign the pointer to the sub-polyline
10067 poly_to_connect_pt = tmp_vector_subpolylines[isub];
10068 // Search for the vertex in the current sub-polyline
10069 found_vertex_index =
10070 get_connected_vertex_number_on_destination_polyline(
10071 poly_to_connect_pt,
10072 shd_bnd_left_vertex,
10073 vertex_index);
10074 // If we have found the vertex to connect then break the
10075 // loop
10076 if (found_vertex_index)
10077 {
10078 // But first save the subpoly number (chunk), that
10079 // will be used to perform the connection
10080 sub_poly_to_connect = isub;
10081 break;
10082 } // if (found_vertex_index)
10083
10084 } // if (not overlaped by shared boundary)
10085
10086 } // for (isub < nsub_poly)
10087
10088 } // if (!found_vertex_index)
10089
10090#ifdef PARANOID
10091 // If we could not find the vertex index to connect then
10092 // we are in trouble
10093 if (!found_vertex_index)
10094 {
10095 std::stringstream error;
10096 error
10097 << "The current shared boundary (" << bound_id << ") was "
10098 << "marked to have a connection\nto the left with the "
10099 << "boundary (" << uconnection_to_the_left << ").\n"
10100 << "This last boundary is marked to be overlaped by "
10101 << "shared boundaries\n"
10102 << "The problem is that the left vertex of the current\n"
10103 << "shared boundary is not in the list of vertices of "
10104 << "the\nboundary to connect.\n\n"
10105 << "This is the left vertex of the current shared "
10106 << "boundary\n"
10107 << "Left vertex: (" << shd_bnd_left_vertex[0] << ", "
10108 << shd_bnd_left_vertex[1] << ")\n\n"
10109 << "This is the list of vertices on the destination "
10110 << "boundary (only those subpolylines not marked as "
10111 << "overlaped by\nshared boundaries)\n";
10112 for (unsigned p = 0; p < nsub_poly; p++)
10113 {
10114 if (!boundary_marked_as_shared_boundary(
10115 uconnection_to_the_left, p))
10116 {
10117 error << "Subpolyline #(" << p << ")\n";
10118 poly_to_connect_pt = tmp_vector_subpolylines[p];
10119 const unsigned n_v = poly_to_connect_pt->nvertex();
10120 for (unsigned i = 0; i < n_v; i++)
10121 {
10122 Vector<double> cvertex =
10123 poly_to_connect_pt->vertex_coordinate(i);
10124 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10125 << cvertex[1] << ")\n";
10126 }
10127 } // Not marked as overlaped
10128 } // for (p < nsub_poly)
10129 error << "\nThis is the list of vertices of the shared "
10130 << "polylines that overlap\nthe internal "
10131 << "boundary\n";
10132 Vector<unsigned> dst_shd_bnd_ids;
10133 get_shared_boundaries_overlapping_internal_boundary(
10134 uconnection_to_the_left, dst_shd_bnd_ids);
10135 const unsigned n_shd_bnd_overlap_int_bnd =
10136 dst_shd_bnd_ids.size();
10137 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10138 {
10139 const unsigned new_connection_to_the_left =
10140 dst_shd_bnd_ids[ss];
10141 poly_to_connect_pt =
10142 boundary_polyline_pt(new_connection_to_the_left);
10143 if (poly_to_connect_pt != 0)
10144 {
10145 const unsigned shd_bnd_id_overlap =
10146 poly_to_connect_pt->boundary_id();
10147 error << "Shared boundary id(" << shd_bnd_id_overlap
10148 << ")\n";
10149 const unsigned n_v = poly_to_connect_pt->nvertex();
10150 for (unsigned i = 0; i < n_v; i++)
10151 {
10152 Vector<double> cvertex =
10153 poly_to_connect_pt->vertex_coordinate(i);
10154 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10155 << cvertex[1] << ")\n";
10156 }
10157 } // if (poly_to_connect_pt != 0)
10158 } // for (ss < n_shd_bnd_overlap_int_bnd)
10159
10160 throw OomphLibError(
10161 error.str(),
10162 "TriangleMesh::create_shared_polylines_connections()",
10163 OOMPH_EXCEPTION_LOCATION);
10164 } // if (!found_vertex_index)
10165#endif
10166
10167 // Create the connection, the left vertex of the current
10168 // shared boundary is connected with the vertex_index-th
10169 // vertex of sub_poly_to_connect-th subpolyline of the
10170 // destination boundary
10172 poly_to_connect_pt, vertex_index, sub_poly_to_connect);
10173
10174 } // else if (!connecting_to_an_overlaped_boundary)
10175
10176 } // else if (!connecting_to_an_split_boundary)
10177
10178 } // if (connection_to_the_left != -1)
10179
10180 // --------------------------------------------------------------
10181 // Connection to the right
10182 if (is_connected_to_the_right)
10183 {
10184 // Get the unsigned version of the bound id to connect to
10185 // the right
10186 const unsigned uconnection_to_the_right =
10187 shd_poly_pt->final_vertex_connected_bnd_id();
10188
10189 // The pointer to the boundary to connect
10190 TriangleMeshPolyLine* poly_to_connect_pt = 0;
10191
10192 // Flag to indicate we are trying to connect to an split
10193 // boundary
10194 bool connecting_to_an_split_boundary = false;
10195
10196 // Flag to indicate we are trying to connecto to an internal
10197 // boundary that is overlaped by a shared boundary
10198 bool connecting_to_an_overlaped_boundary = false;
10199
10200 // Check if the connection is with itself
10201 if (uconnection_to_the_right == bound_id)
10202 {
10203 // Set the pointer to the polyline to connect
10204 poly_to_connect_pt = shd_poly_pt;
10205 }
10206 else
10207 {
10208 // Get the initial shared boundary ids
10209 const unsigned initial_shd_bnd_id = initial_shared_boundary_id();
10210 // Check if the boundary to connect is a shared polyline
10211 if (uconnection_to_the_right >= initial_shd_bnd_id)
10212 {
10213 // Get the polyline pointer representing the destination
10214 // boundary
10215 poly_to_connect_pt =
10216 boundary_polyline_pt(uconnection_to_the_right);
10217 } // if (uconnection_to_the_left >= initial_shd_bnd_id)
10218 else
10219 {
10220 // If we are going to connect to an original boundary
10221 // verify if the boundary was splitted during the
10222 // distribution process to consider all the chunks
10223 // (sub-polylines) of the boundary
10224 if (boundary_was_splitted(uconnection_to_the_right))
10225 {
10226 connecting_to_an_split_boundary = true;
10227 } // if (boundary_was_splitted(uconnection_to_the_right))
10228
10229 // If we are going to connect to an original boundary
10230 // verify if the boundary, or any of its chunks is
10231 // marked to be overlapped by a shared boundary, if that
10232 // is the case we first check for connections in the
10233 // shared boundary that overlaps the internal boundary,
10234 // or the chunks, and then check for connections in the
10235 // original boundary
10236 if (connecting_to_an_split_boundary)
10237 {
10238 // Get the number of chucks that represent the
10239 // destination boundary
10240 const unsigned n_sub_poly =
10241 nboundary_subpolylines(uconnection_to_the_right);
10242 // Now loop over the chunks of the destination
10243 // boundary and if any of them is marked to be
10244 // overlaped by a shared boundary then set the flag
10245 // and break the loop
10246 for (unsigned ii = 0; ii < n_sub_poly; ii++)
10247 {
10248 if (boundary_marked_as_shared_boundary(
10249 uconnection_to_the_right, ii))
10250 {
10251 // Mark the boundary as being overlaped by a
10252 // shared boundary
10253 connecting_to_an_overlaped_boundary = true;
10254 // Break, no need to look for more overlapings
10255 break;
10256 } // if (boundary_marked_as_shared_boundary(...))
10257 } // for (ii < n_sub_poly)
10258 } // if (connecting_to_an_split_boundary)
10259 else
10260 {
10261 // If not connecting to an split boundary then check
10262 // if the whole destination boundary is overlaped by
10263 // an internal boundary
10264 if (boundary_marked_as_shared_boundary(
10265 uconnection_to_the_right, 0))
10266 {
10267 // Mark the boundary as being overlaped by a shared
10268 // boundary
10269 connecting_to_an_overlaped_boundary = true;
10270 } // if (boundary_marked_as_shared_boundary(...))
10271 } // else if (connecting_to_an_split_boundary)
10272
10273 // If we are connecting neither to an split boundary nor
10274 // an overlaped boundary then get the pointer to the
10275 // original boundary
10276 if (!(connecting_to_an_split_boundary ||
10277 connecting_to_an_overlaped_boundary))
10278 {
10279 // Get the polyline pointer representing the
10280 // destination boundary
10281 poly_to_connect_pt =
10282 boundary_polyline_pt(uconnection_to_the_right);
10283 } // else if (NOT split, NOT overlaped)
10284 } // else if (uconnection_to_the_right >= initial_shd_bnd_id)
10285
10286 } // else if (uconnection_to_the_right == bound_id)
10287
10288#ifdef PARANOID
10289 // If we are not connecting to an original boundary
10290 // (connecting to the same shared boundary or to another
10291 // shared boundary) then the boundary should not be marked
10292 // as split
10293 if (!connecting_to_an_split_boundary)
10294 {
10295 if (boundary_was_splitted(uconnection_to_the_right))
10296 {
10297 std::stringstream error;
10298 error
10299 << "The current shared boundary (" << bound_id << ") was "
10300 << "marked to have a connection\nto the right with the "
10301 << "boundary (" << uconnection_to_the_right << ").\n"
10302 << "The problem is that the destination boundary (possibly\n"
10303 << "another shared boundary) is marked to be split\n"
10304 << "There should not be split shared boundaries\n\n";
10305 throw OomphLibError(
10306 error.str(),
10307 "TriangleMesh::create_shared_polylines_connections()",
10308 OOMPH_EXCEPTION_LOCATION);
10309 }
10310 } // if (!connecting_to_an_split_boundary)
10311#endif
10312
10313 // Now look for the vertex number on the destination
10314 // boundary(ies) -- in case that the boundary was split ---
10315
10316 // Do not check for same orientation, that was previously
10317 // worked by interchanging the connections boundaries (if
10318 // necessary)
10319
10320 // Get the right vertex in the shared boundary
10321 Vector<double> shd_bnd_right_vertex =
10322 shd_poly_pt->vertex_coordinate(n_vertex - 1);
10323
10324 // If the boundary was not split then inmediately look for
10325 // the vertex index in the destination boundary
10326 if (!connecting_to_an_split_boundary)
10327 {
10328 // ... check if the boundary is marked to be overlaped by
10329 // a shared boundary
10330 if (!connecting_to_an_overlaped_boundary)
10331 {
10332 // If that is not the case then we can safely look for
10333 // the vertex number on the destination boundar
10334
10335 unsigned vertex_index = 0;
10336 const bool found_vertex_index =
10337 get_connected_vertex_number_on_destination_polyline(
10338 poly_to_connect_pt, shd_bnd_right_vertex, vertex_index);
10339
10340 // If we could not find the vertex index to connect then
10341 // we are in trouble
10342 if (!found_vertex_index)
10343 {
10344 std::stringstream error;
10345 error
10346 << "The current shared boundary (" << bound_id << ") was "
10347 << "marked to have a connection\nto the right with the "
10348 << "boundary (" << uconnection_to_the_right << ").\n"
10349 << "The problem is that the right vertex of the current\n"
10350 << "shared boundary is not in the list of vertices of the\n"
10351 << "boundary to connect.\n\n"
10352 << "This is the right vertex of the current shared "
10353 "boundary\n"
10354 << "Right vertex: (" << shd_bnd_right_vertex[0] << ", "
10355 << shd_bnd_right_vertex[1] << ")\n\n"
10356 << "This is the list of vertices on the destination "
10357 "boundary\n";
10358 const unsigned n_v = poly_to_connect_pt->nvertex();
10359 for (unsigned i = 0; i < n_v; i++)
10360 {
10361 Vector<double> cvertex =
10362 poly_to_connect_pt->vertex_coordinate(i);
10363 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10364 << cvertex[1] << ")\n";
10365 }
10366 throw OomphLibError(
10367 error.str(),
10368 "TriangleMesh::create_shared_polylines_connections()",
10369 OOMPH_EXCEPTION_LOCATION);
10370 } // if (!found_vertex_index)
10371
10372 // Create the connection, the right vertex of the current
10373 // shared boundary is connected with the vertex_index-th
10374 // vertex on the destination boundary
10376 poly_to_connect_pt, vertex_index);
10377
10378 } // if (!connecting_to_an_overlaped_boundary)
10379 else
10380 {
10381 // If the boundary is marked to be overlaped by a shared
10382 // boundary then get that shared boundary and look for
10383 // the connection in that boundary
10384
10385 // The vertex where to store the index to connect
10386 unsigned vertex_index = 0;
10387 // A flag to indicate if the connection was found
10388 bool found_vertex_index = false;
10389
10390 // Get the shared boundary id that is overlaping the
10391 // internal boundary
10392 Vector<unsigned> dst_shd_bnd_ids;
10393 get_shared_boundaries_overlapping_internal_boundary(
10394 uconnection_to_the_right, dst_shd_bnd_ids);
10395
10396 // Get the number of shared polylines that were found to
10397 // overlap the internal boundary
10398 const unsigned n_shd_bnd_overlap_int_bnd =
10399 dst_shd_bnd_ids.size();
10400
10401 // Loop over the shared boundaries that overlap the
10402 // internal boundary and look for the vertex to connect
10403 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10404 {
10405 // Get the shared polyline
10406 const unsigned new_connection_to_the_right =
10407 dst_shd_bnd_ids[ss];
10408
10409 // Get the shared polyline that is overlaping the
10410 // internal boundary
10411 poly_to_connect_pt =
10412 boundary_polyline_pt(new_connection_to_the_right);
10413
10414 if (poly_to_connect_pt != 0)
10415 {
10416 // Look for the vertex number in the destination
10417 // shared polyline
10418 found_vertex_index =
10419 get_connected_vertex_number_on_destination_polyline(
10420 poly_to_connect_pt, shd_bnd_right_vertex, vertex_index);
10421 } // if (poly_to_connect_pt!=0)
10422
10423 // If we have found the vertex to connect then
10424 // break the loop
10425 if (found_vertex_index)
10426 {
10427 break;
10428 } // if (found_vertex_index)
10429
10430 } // for (ss < n_shd_bnd_overlaping_int_bnd)
10431
10432#ifdef PARANOID
10433 // If we could not find the vertex index to connect then
10434 // we are in trouble
10435 if (!found_vertex_index)
10436 {
10437 std::stringstream error;
10438 error
10439 << "The current shared boundary (" << bound_id << ") was "
10440 << "marked to have a connection\nto the right with the "
10441 << "boundary (" << uconnection_to_the_right << ").\n"
10442 << "This last boundary is marked to be overlaped by "
10443 << "shared boundaries\n"
10444 << "The problem is that the right vertex of the current\n"
10445 << "shared boundary is not in the list of vertices of the\n"
10446 << "boundary to connect.\n\n"
10447 << "This is the right vertex of the current shared "
10448 "boundary\n"
10449 << "Right vertex: (" << shd_bnd_right_vertex[0] << ", "
10450 << shd_bnd_right_vertex[1] << ")\n\n"
10451 << "This is the list of vertices on the destination "
10452 << "boundary\n";
10453 Vector<unsigned> dst_shd_bnd_ids;
10454 get_shared_boundaries_overlapping_internal_boundary(
10455 uconnection_to_the_right, dst_shd_bnd_ids);
10456 const unsigned n_shd_bnd_overlap_int_bnd =
10457 dst_shd_bnd_ids.size();
10458 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10459 {
10460 const unsigned new_connection_to_the_right =
10461 dst_shd_bnd_ids[ss];
10462 poly_to_connect_pt =
10463 boundary_polyline_pt(new_connection_to_the_right);
10464 if (poly_to_connect_pt != 0)
10465 {
10466 const unsigned shd_bnd_id_overlap =
10467 poly_to_connect_pt->boundary_id();
10468 error << "Shared boundary id(" << shd_bnd_id_overlap
10469 << ")\n";
10470 const unsigned n_v = poly_to_connect_pt->nvertex();
10471 for (unsigned i = 0; i < n_v; i++)
10472 {
10473 Vector<double> cvertex =
10474 poly_to_connect_pt->vertex_coordinate(i);
10475 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10476 << cvertex[1] << ")\n";
10477 }
10478 } // if (poly_to_connect_pt != 0)
10479 } // for (ss < n_shd_bnd_overlap_int_bnd)
10480
10481 throw OomphLibError(
10482 error.str(),
10483 "TriangleMesh::create_shared_polylines_connections()",
10484 OOMPH_EXCEPTION_LOCATION);
10485
10486 } // if (!found_vertex_index)
10487#endif
10488
10489 // Create the connection, the right vertex of the
10490 // current shared boundary is connected with the
10491 // vertex_index-th vertex on the destination boundary
10493 poly_to_connect_pt, vertex_index);
10494
10495 } // else if (!connecting_to_an_overlaped_boundary)
10496
10497 } // if (!connecting_to_an_split_boundary)
10498 else
10499 {
10500 // If the boundary was split then we need to look for the
10501 // vertex in the sub-polylines
10502
10503 // Get the sub-polylines vector
10504 Vector<TriangleMeshPolyLine*> tmp_vector_subpolylines =
10505 boundary_subpolylines(uconnection_to_the_right);
10506
10507 // Get the number of sub-polylines
10508 const unsigned nsub_poly = tmp_vector_subpolylines.size();
10509#ifdef PARANOID
10510 if (nsub_poly <= 1)
10511 {
10512 std::ostringstream error_message;
10513 error_message
10514 << "The boundary (" << uconnection_to_the_right << ") was "
10515 << "marked to be splitted but\n"
10516 << "there are only (" << nsub_poly << ") polylines to "
10517 << "represent it.\n";
10518 throw OomphLibError(
10519 error_message.str(),
10520 "TriangleMesh::create_shared_polylines_connections()",
10521 OOMPH_EXCEPTION_LOCATION);
10522 } // if (nsub_poly <= 1)
10523#endif
10524
10525 // We need to check if the boundary is marked to be
10526 // overlaped by an internal boundary, if that is the case
10527 // we need to check for each indivual subpolyline, and for
10528 // those overlaped by a shared polyline look for the
10529 // vertex in the shared polyline representation instead of
10530 // the original subpolyline
10531
10532 // ... check if the boundary is marked to be overlaped by
10533 // a shared boundary
10534 if (!connecting_to_an_overlaped_boundary)
10535 {
10536 // We can work without checking the subpolylines
10537 // individually
10538
10539 // The vertex where to store the index to connect
10540 unsigned vertex_index = 0;
10541 // The subpoly number to connect
10542 unsigned sub_poly_to_connect = 0;
10543 // A flag to indicate if the connection was found
10544 bool found_vertex_index = false;
10545
10546 // Look for the vertex number to connect on each of the
10547 // subpolyines
10548 for (unsigned isub = 0; isub < nsub_poly; isub++)
10549 {
10550 // Assign the pointer to the sub-polyline
10551 poly_to_connect_pt = tmp_vector_subpolylines[isub];
10552 // Search for the vertex in the current sub-polyline
10553 found_vertex_index =
10554 get_connected_vertex_number_on_destination_polyline(
10555 poly_to_connect_pt, shd_bnd_right_vertex, vertex_index);
10556 // If we have found the vertex to connect then break the
10557 // loop
10558 if (found_vertex_index)
10559 {
10560 // But first save the subpoly number (chunk), that
10561 // will be used to perform the connection
10562 sub_poly_to_connect = isub;
10563 break;
10564 } // if (found_vertex_index)
10565 } // for (isub < nsub_poly)
10566
10567#ifdef PARANOID
10568 // If we could not find the vertex index to connect then
10569 // we are in trouble
10570 if (!found_vertex_index)
10571 {
10572 std::stringstream error;
10573 error
10574 << "The current shared boundary (" << bound_id << ") was "
10575 << "marked to have a connection\nto the right with the "
10576 << "boundary (" << uconnection_to_the_right << ").\n"
10577 << "The problem is that the right vertex of the current\n"
10578 << "shared boundary is not in the list of vertices of any\n"
10579 << "of the sub polylines that represent the boundary to\n"
10580 << "connect.\n\n"
10581 << "This is the right vertex of the current shared "
10582 "boundary\n"
10583 << "Right vertex: (" << shd_bnd_right_vertex[0] << ", "
10584 << shd_bnd_right_vertex[1] << ")\n\n"
10585 << "This is the list of vertices on the destination "
10586 << "boundary\n";
10587 for (unsigned p = 0; p < nsub_poly; p++)
10588 {
10589 error << "Subpolyline #(" << p << ")\n";
10590 poly_to_connect_pt = tmp_vector_subpolylines[p];
10591 const unsigned n_v = poly_to_connect_pt->nvertex();
10592 for (unsigned i = 0; i < n_v; i++)
10593 {
10594 Vector<double> cvertex =
10595 poly_to_connect_pt->vertex_coordinate(i);
10596 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10597 << cvertex[1] << ")\n";
10598 }
10599 } // for (p < nsub_poly)
10600 throw OomphLibError(
10601 error.str(),
10602 "TriangleMesh::create_shared_polylines_connections()",
10603 OOMPH_EXCEPTION_LOCATION);
10604 } // if (!found_vertex_index)
10605#endif
10606
10607 // Create the connection, the right vertex of the current
10608 // shared boundary is connected with the vertex_index-th
10609 // vertex of sub_poly_to_connect-th subpolyline of the
10610 // destination boundary
10612 poly_to_connect_pt, vertex_index, sub_poly_to_connect);
10613
10614 } // if (!connecting_to_an_overlaped_boundary)
10615 else
10616 {
10617 // We first look on the shared boundaries that overlap
10618 // the internal boundaries and the look for the
10619 // sub-polylines that are not marked as being overlaped
10620 // by shared boundaries
10621
10622 // The vertex where to store the index to connect
10623 unsigned vertex_index = 0;
10624 // The subpoly number to connect
10625 unsigned sub_poly_to_connect = 0;
10626 // A flag to indicate if the connection was found
10627 bool found_vertex_index = false;
10628
10629 // Get the shared boundaries id that are overlaping the
10630 // internal boundary
10631 Vector<unsigned> dst_shd_bnd_ids;
10632 get_shared_boundaries_overlapping_internal_boundary(
10633 uconnection_to_the_right, dst_shd_bnd_ids);
10634
10635 // Get the number of shared polylines that were found to
10636 // overlap the internal boundary
10637 const unsigned n_shd_bnd_overlap_int_bnd =
10638 dst_shd_bnd_ids.size();
10639
10640 // Loop over the shared boundaries that overlap the
10641 // internal boundary and look for the vertex to connect
10642 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10643 {
10644 // Get the shared polyline
10645 const unsigned new_connection_to_the_right =
10646 dst_shd_bnd_ids[ss];
10647
10648 // Make sure that the destination polyline is not the
10649 // same as the current shared polyline
10650 if (bound_id != new_connection_to_the_right)
10651 {
10652 // Get the shared polyline that is overlaping the
10653 // internal boundary
10654 poly_to_connect_pt =
10655 boundary_polyline_pt(new_connection_to_the_right);
10656
10657 if (poly_to_connect_pt != 0)
10658 {
10659 // Look for the vertex number in the destination
10660 // shared polyline
10661 found_vertex_index =
10662 get_connected_vertex_number_on_destination_polyline(
10663 poly_to_connect_pt,
10664 shd_bnd_right_vertex,
10665 vertex_index);
10666 } // if (poly_to_connect_pt != 0)
10667
10668 // If we have found the vertex to connect then
10669 // break the loop
10670 if (found_vertex_index)
10671 {
10672 break;
10673 } // if (found_vertex_index)
10674
10675 } // if (bound_id != new_connection_to_the_right)
10676
10677 } // for (ss < n_shd_bnd_overlaping_int_bnd)
10678
10679 // If we have not yet found the vertex then look for it
10680 // in the sub-polylines that are not overlaped by shared
10681 // boundaries
10682 if (!found_vertex_index)
10683 {
10684 // Look for the vertex number to connect on each of
10685 // the subpolyines
10686 for (unsigned isub = 0; isub < nsub_poly; isub++)
10687 {
10688 // Only work with those sub-polylines that are not
10689 // overlaped by shared boundaries
10690 if (!boundary_marked_as_shared_boundary(
10691 uconnection_to_the_right, isub))
10692 {
10693 // Assign the pointer to the sub-polyline
10694 poly_to_connect_pt = tmp_vector_subpolylines[isub];
10695 // Search for the vertex in the current sub-polyline
10696 found_vertex_index =
10697 get_connected_vertex_number_on_destination_polyline(
10698 poly_to_connect_pt,
10699 shd_bnd_right_vertex,
10700 vertex_index);
10701 // If we have found the vertex to connect then break the
10702 // loop
10703 if (found_vertex_index)
10704 {
10705 // But first save the subpoly number (chunk), that
10706 // will be used to perform the connection
10707 sub_poly_to_connect = isub;
10708 break;
10709 } // if (found_vertex_index)
10710
10711 } // if (not overlaped by shared boundary)
10712
10713 } // for (isub < nsub_poly)
10714
10715 } // if (!found_vertex_index)
10716
10717#ifdef PARANOID
10718 // If we could not find the vertex index to connect then
10719 // we are in trouble
10720 if (!found_vertex_index)
10721 {
10722 std::stringstream error;
10723 error
10724 << "The current shared boundary (" << bound_id << ") was "
10725 << "marked to have a connection\nto the right with the "
10726 << "boundary (" << uconnection_to_the_right << ").\n"
10727 << "This last boundary is marked to be overlaped by "
10728 << "shared boundaries\n"
10729 << "The problem is that the right vertex of the current\n"
10730 << "shared boundary is not in the list of vertices of "
10731 << "the\nboundary to connect.\n\n"
10732 << "This is the right vertex of the current shared "
10733 << "boundary\n"
10734 << "Right vertex: (" << shd_bnd_right_vertex[0] << ", "
10735 << shd_bnd_right_vertex[1] << ")\n\n"
10736 << "This is the list of vertices on the destination "
10737 << "boundary (only those subpolylines not marked as "
10738 << "overlaped by\nshared boundaries)\n";
10739 for (unsigned p = 0; p < nsub_poly; p++)
10740 {
10741 if (!boundary_marked_as_shared_boundary(
10742 uconnection_to_the_right, p))
10743 {
10744 error << "Subpolyline #(" << p << ")\n";
10745 poly_to_connect_pt = tmp_vector_subpolylines[p];
10746 const unsigned n_v = poly_to_connect_pt->nvertex();
10747 for (unsigned i = 0; i < n_v; i++)
10748 {
10749 Vector<double> cvertex =
10750 poly_to_connect_pt->vertex_coordinate(i);
10751 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10752 << cvertex[1] << ")\n";
10753 }
10754 } // Not marked as overlaped
10755 } // for (p < nsub_poly)
10756 error << "\nThis is the list of vertices of the shared "
10757 << "polylines that overlap\nthe internal "
10758 << "boundary\n";
10759 Vector<unsigned> dst_shd_bnd_ids;
10760 get_shared_boundaries_overlapping_internal_boundary(
10761 uconnection_to_the_right, dst_shd_bnd_ids);
10762 const unsigned n_shd_bnd_overlap_int_bnd =
10763 dst_shd_bnd_ids.size();
10764 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10765 {
10766 const unsigned new_connection_to_the_right =
10767 dst_shd_bnd_ids[ss];
10768 poly_to_connect_pt =
10769 boundary_polyline_pt(new_connection_to_the_right);
10770 if (poly_to_connect_pt != 0)
10771 {
10772 const unsigned shd_bnd_id_overlap =
10773 poly_to_connect_pt->boundary_id();
10774 error << "Shared boundary id(" << shd_bnd_id_overlap
10775 << ")\n";
10776 const unsigned n_v = poly_to_connect_pt->nvertex();
10777 for (unsigned i = 0; i < n_v; i++)
10778 {
10779 Vector<double> cvertex =
10780 poly_to_connect_pt->vertex_coordinate(i);
10781 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10782 << cvertex[1] << ")\n";
10783 }
10784 } // if (poly_to_connect_pt != 0)
10785 } // for (ss < n_shd_bnd_overlap_int_bnd)
10786
10787 throw OomphLibError(
10788 error.str(),
10789 "TriangleMesh::create_shared_polylines_connections()",
10790 OOMPH_EXCEPTION_LOCATION);
10791 } // if (!found_vertex_index)
10792#endif
10793
10794 // Create the connection, the left vertex of the current
10795 // shared boundary is connected with the vertex_index-th
10796 // vertex of sub_poly_to_connect-th subpolyline of the
10797 // destination boundary
10799 poly_to_connect_pt, vertex_index, sub_poly_to_connect);
10800
10801 } // else if (!connecting_to_an_overlaped_boundary)
10802
10803 } // else if (!connecting_to_an_split_boundary)
10804
10805 } // if (connection_to_the_right != -1)
10806
10807 } // if (connection_to_the_left != -1 || connection_to_the_right != -1)
10808
10809 } // for (ipoly < npoly)
10810
10811 } // for (icurve < ncurves)
10812 }
10813
10814 //=======================================================================
10815 // Compute the holes left by the halo elements, those adjacent
10816 // to the shared boundaries
10817 //=======================================================================
10818 template<class ELEMENT>
10820 Vector<Vector<double>>& output_holes_coordinates)
10821 {
10822 // Storage for number of processors and current processor
10823 const unsigned n_proc = this->communicator_pt()->nproc();
10824 const unsigned my_rank = this->communicator_pt()->my_rank();
10825
10826 // Mark those done elements, so we do not repeat any coordinate left
10827 // by repeated halo elements
10828 std::map<FiniteElement*, bool> done_ele;
10829
10830 // Loop over the processors and get the shared boundaries ids that
10831 // the current processor has with the other processors
10832 for (unsigned iproc = 0; iproc < n_proc; iproc++)
10833 {
10834 // There are shared boundaries only with the other processors
10835 if (iproc != my_rank)
10836 {
10837 // Get the number of shared boundaries with the iproc
10838 const unsigned n_shd_bnd_iproc = nshared_boundaries(my_rank, iproc);
10839
10840#ifdef PARANOID
10841 // Get the number of shared boundaries with the iproc, but
10842 // reversing the indexes
10843 const unsigned n_shd_bnd_iproc_rev = nshared_boundaries(iproc, my_rank);
10844 if (n_shd_bnd_iproc != n_shd_bnd_iproc_rev)
10845 {
10846 std::ostringstream error_stream;
10847 error_stream
10848 << "The number of shared boundaries of processor (" << my_rank
10849 << ") with processor(" << iproc << "): (" << n_shd_bnd_iproc
10850 << ")\n"
10851 << "is different from the number of shared boundaries of "
10852 << "processor (" << iproc << ")\nwith processor (" << my_rank
10853 << "): (" << n_shd_bnd_iproc << ")\n\n";
10854 throw OomphLibError(error_stream.str(),
10855 OOMPH_CURRENT_FUNCTION,
10856 OOMPH_EXCEPTION_LOCATION);
10857
10858 } // if (n_shd_bnd_iproc != n_shd_bnd_iproc_rev)
10859#endif
10860
10861 // Loop over the shared boundaries ids
10862 for (unsigned i = 0; i < n_shd_bnd_iproc; i++)
10863 {
10864 // Get the shared boundary id
10865 const unsigned shd_bnd_id = shared_boundaries_ids(my_rank, iproc, i);
10866
10867 // Get the number of shared boundary elements
10868 const unsigned n_shd_bnd_ele = nshared_boundary_element(shd_bnd_id);
10869
10870 // Loop over the shared boundary elements
10871 for (unsigned e = 0; e < n_shd_bnd_ele; e++)
10872 {
10873 // Get the shared boundary element
10874 FiniteElement* ele_pt = shared_boundary_element_pt(shd_bnd_id, e);
10875
10876 // Only work with halo elements
10877 if (ele_pt->is_halo())
10878 {
10879 // If the element has not been visited
10880 if (!done_ele[ele_pt])
10881 {
10882 // Get the number of nodes
10883 const unsigned n_nodes = ele_pt->nnode();
10884
10885 // Compute the centroid of the element
10886 Vector<double> element_centroid(2, 0.0);
10887 // Loop over the nodes
10888 for (unsigned k = 0; k < n_nodes; k++)
10889 {
10890 Node* tmp_node_pt = ele_pt->node_pt(k);
10891 // Loop over the dimension
10892 for (unsigned d = 0; d < 2; d++)
10893 {
10894 element_centroid[d] += tmp_node_pt->x(d);
10895 } // for (d < 2)
10896 } // for (k < n_nodes)
10897
10898 // Average the data
10899 for (unsigned d = 0; d < 2; d++)
10900 {
10901 element_centroid[d] = element_centroid[d] / (double)n_nodes;
10902 } // for (d < 2)
10903
10904 // Add the centroid to the output holes
10905 output_holes_coordinates.push_back(element_centroid);
10906
10907 } // if (!done_ele[ele_pt])
10908
10909 } // if (ele_pt->is_halo())
10910
10911 } // for1 (e < n_shd_bnd_ele)
10912
10913 } // for (i < n_shd_bnd_iproc)
10914
10915 } // if (iproc != my_rank)
10916
10917 } // for (iproc < n_proc)
10918 }
10919
10920 //======================================================================
10921 // Keeps those vertices that define a hole, those that are
10922 // inside closed internal boundaries in the new polygons that define
10923 // the domain. Delete those outside/inside the outer polygons (this
10924 // is required since Triangle can not deal with vertices that define
10925 // holes outside the new outer polygons of the domain)
10926 //======================================================================
10927 template<class ELEMENT>
10929 Vector<TriangleMeshPolygon*>& polygons_pt,
10930 Vector<Vector<double>>& output_holes_coordinates)
10931 {
10932 // General strategy
10933
10934 // 1) Identify the inner closed boundaries
10935
10936 // 2) Separate the vertices in three groups
10937
10938 // --- 2.1) The vertices inside the inner closed boundaries, these
10939 // are not deleted because they define holes
10940
10941 // --- 2.2) The vertices outside the outer boundaries, these are
10942 // deleted only if they are outside the convex hull defined
10943 // by all the polygons
10944
10945 // --- 2.3) Any other vertex is deleted
10946
10947 // Get the number of input holes
10948 const unsigned n_input_holes = output_holes_coordinates.size();
10949
10950 // Only do something if there are holes
10951 if (n_input_holes == 0)
10952 {
10953 return;
10954 }
10955
10956 // Get the number of input polygons
10957 const unsigned n_polygons = polygons_pt.size();
10958
10959 // Store the vertices of all the input polygons
10960 // vertices_polygons[x][ ][ ]: Polygon number
10961 // vertices_polygons[ ][x][ ]: Vertex number
10962 // vertices_polygons[ ][ ][x]: Vertex coordinate
10963 Vector<Vector<Vector<double>>> vertices_polygons(n_polygons);
10964
10965 // Loop over all the polygons and get the vertices
10966 for (unsigned p = 0; p < n_polygons; p++)
10967 {
10968 // Get the number of polylines associated to the polygon
10969 const unsigned n_polylines = polygons_pt[p]->npolyline();
10970 // Loop over the polylines and get the vertices
10971 for (unsigned pp = 0; pp < n_polylines; pp++)
10972 {
10973 // Get the polyline
10974 const TriangleMeshPolyLine* tmp_poly_pt =
10975 polygons_pt[p]->polyline_pt(pp);
10976 // Get the number of vertices in the polyline
10977 const unsigned n_vertices = tmp_poly_pt->nvertex();
10978 // Loop over the vertices but only add (n_vertices-1) vertices,
10979 // the last vertex of polyline (pp) is the first vertex of
10980 // polyline (pp+1)
10981 for (unsigned v = 0; v < n_vertices - 1; v++)
10982 {
10983 // Get the current vertex
10984 Vector<double> current_vertex = tmp_poly_pt->vertex_coordinate(v);
10985 vertices_polygons[p].push_back(current_vertex);
10986 } // for (v < nvertex)
10987 } // for (p < nouter_polylines)
10988 } // for (p < n_polygons)
10989
10990 // -------------------------------------------------------------------
10991 // 1) Identify the inner closed boundaries
10992 // -------------------------------------------------------------------
10993
10994 // A container that indicates if a given polygon should be
10995 // considered as an outer or as an inner polygon. By default all the
10996 // polygons are considered as outer polygons
10997 std::vector<bool> is_outer_polygon(n_polygons, true);
10998
10999 // We only check for innner polygons if there are more than one
11000 // polygon
11001 if (n_polygons > 1)
11002 {
11003 // Propose an inner polygon, if one of the middle points of its
11004 // edges lies inside any other polygon then the proposed inner
11005 // polygon is marked as an internal polygon
11006
11007 // Pre-compute the middle points of the edges in the polygons
11008 Vector<Vector<Vector<double>>> polygon_edge_middle_vertex(n_polygons);
11009
11010 for (unsigned p = 0; p < n_polygons; p++)
11011 {
11012 // Temporary store the vertices of the proposed inner polygon
11013 Vector<Vector<double>> tmp_inner_polygon = vertices_polygons[p];
11014
11015 // Get the number of vertices in the current proposed inner polygon
11016 const unsigned n_vertices = tmp_inner_polygon.size();
11017
11018 // Resize with the number of edges in the polygon
11019 polygon_edge_middle_vertex[p].resize(n_vertices - 1);
11020
11021 // Loop over the vertices and compute the middle point in the edge
11022 // that joins each pair of contiguous vertices
11023 for (unsigned e = 0; e < n_vertices - 1; e++)
11024 {
11025 // The dimension
11026 const unsigned dim = 2;
11027 polygon_edge_middle_vertex[p][e].resize(dim);
11028 for (unsigned d = 0; d < dim; d++)
11029 {
11030 polygon_edge_middle_vertex[p][e][d] =
11031 (tmp_inner_polygon[e][d] + tmp_inner_polygon[e + 1][d]) / 2.0;
11032 } // for (d < 2)
11033
11034 } // for (e < n_vertices - 1)
11035
11036 } // for (p < n_polygons)
11037
11038 // Loop over the polygons and for every loop propose a different
11039 // inner polygon
11040 for (unsigned idx_inner = 0; idx_inner < n_polygons; idx_inner++)
11041 {
11042 // Flag to indicate that ONE of the middle edge vertices of the
11043 // proposed inner polygon is inside another polygon, this will
11044 // set the proposed inner polygon as an actual inner polygon
11045 bool is_inner_polygon = false;
11046
11047 // Loop over all the polygons, except the proposed one and check
11048 // if all the middle edges of its edges are inside any other
11049 // polygon
11050 for (unsigned i = 0; i < n_polygons; i++)
11051 {
11052 // Do not check with the polygon itself
11053 if (i != idx_inner)
11054 {
11055 // Get the number of edges of the proposed inner polygon
11056 const unsigned n_edges =
11057 polygon_edge_middle_vertex[idx_inner].size();
11058 // Loop over the middle points in the edges of the current
11059 // proposed inner polygon
11060 for (unsigned e = 0; e < n_edges; e++)
11061 {
11062 // Get the vertex in the current proposed inner polygon
11063 Vector<double> current_vertex =
11064 polygon_edge_middle_vertex[idx_inner][e];
11065 // Check if the current vertex is inside the current i-th
11066 // polygon
11067 const bool is_point_inside = is_point_inside_polygon_helper(
11068 vertices_polygons[i], current_vertex);
11069
11070 // If one point is inside then the polygon is inside the
11071 // i-th polygon
11072 if (is_point_inside)
11073 {
11074 // The polygon is an inner polygon
11075 is_inner_polygon = true;
11076 // Break the loop
11077 break;
11078 } // if (is_point_inside)
11079
11080 } // for (e < n_edges)
11081
11082 } // if (i != idx_inner)
11083
11084 // Are all the vertices of the current proposed inner polygon
11085 // inside the i-th polygon
11086 if (is_inner_polygon)
11087 {
11088 // The current proposed inner polygon is an actual inner
11089 // polygon, and is inside the i-th polygon
11090 break;
11091 }
11092
11093 } // for (i < n_polygons)
11094
11095 // Is the current proposed inner polygon an actual inner polygon
11096 if (is_inner_polygon)
11097 {
11098 // The current proposed inner polygon is a real inner polygon
11099 is_outer_polygon[idx_inner] = false;
11100 }
11101 else
11102 {
11103 // The current proposed inner polygon IS NOT a real inner
11104 // polygon
11105 is_outer_polygon[idx_inner] = true;
11106 }
11107
11108 } // for (idx_outer < npolygons)
11109
11110 } // if (n_polygons > 1)
11111
11112 // Count the number of outer closed boundaries and inner closed
11113 // boundaries
11114 unsigned n_outer_polygons = 0;
11115 unsigned n_inner_polygons = 0;
11116 // Also get the indexes of the inner polygons
11117 Vector<unsigned> index_inner_polygon;
11118 // Loop over the polygons
11119 for (unsigned i = 0; i < n_polygons; i++)
11120 {
11121 if (is_outer_polygon[i])
11122 {
11123 // Increase the counter for outer polygons
11124 n_outer_polygons++;
11125 }
11126 else
11127 {
11128 // Increase the counter for inner polygons
11129 n_inner_polygons++;
11130 // Store the index of the inner polygon
11131 index_inner_polygon.push_back(i);
11132 }
11133 } // for (i < n_polygons)
11134
11135 // -------------------------------------------------------------------
11136 // 2) Separate the vertices in three groups
11137
11138 // --- 2.1) The vertices inside the inner closed boundaries, these are
11139 // not deleted because they define holes
11140
11141 // --- 2.2) The vertices outside the outer boundaries, these are
11142 // deleted only if they are outside the convex hull defined
11143 // by all the polygons
11144
11145 // --- 2.3) Any other vertex is deleted
11146 // -------------------------------------------------------------------
11147
11148 // Keep track of the vertices inside the inner closed boundaries (by
11149 // default all vertices not inside the inner polygons)
11150 std::vector<bool> is_inside_an_inner_polygon(n_input_holes, false);
11151
11152 // Keep track of the vertices outside the outer closed boundaries
11153 // (by default all the vertices are outside the outer polygons)
11154 std::vector<bool> is_outside_the_outer_polygons(n_input_holes, true);
11155
11156 // Keep track of the vertices inside the convex hull (by default
11157 // all the vertices are not inside the convex hull)
11158 std::vector<bool> is_inside_the_convex_hull(n_input_holes, false);
11159
11160 // Mark the vertices inside the inner closed boundaries
11161 Vector<Vector<Vector<double>>> vertex_inside_inner_polygon(
11162 n_inner_polygons);
11163
11164 // -------------------------------------------------------------------
11165 // Loop over the inner polygons and find all the vertices inside
11166 // each one
11167 for (unsigned i = 0; i < n_inner_polygons; i++)
11168 {
11169 // Get the vertex of the inner polygon
11170 const unsigned ii = index_inner_polygon[i];
11171 // Loop over the vertices defining holes, mark and store those
11172 // inside the inner polygon
11173 for (unsigned h = 0; h < n_input_holes; h++)
11174 {
11175 // Check if the vertex has not been already marked as inside
11176 // another polygon
11177 if (!is_inside_an_inner_polygon[h])
11178 {
11179 // Check if the hole is inside the current inner polygon
11180 const bool is_inside_polygon = is_point_inside_polygon_helper(
11181 vertices_polygons[ii], output_holes_coordinates[h]);
11182
11183 // If the vertex is inside the current inner polygon then mark
11184 // it and associate the vertices to the current inner polygon
11185 if (is_inside_polygon)
11186 {
11187 // Set as inside an inner polygon
11188 is_inside_an_inner_polygon[h] = true;
11189 // Associate the vertex to the current inner polygon
11190 vertex_inside_inner_polygon[i].push_back(
11191 output_holes_coordinates[h]);
11192 } // if (is_inside_polygon)
11193
11194 } // if (!is_inside_an_inner_polygon[h])
11195
11196 } // for (h < n_input_holes)
11197
11198 } // for (i < n_polygons)
11199
11200 // -------------------------------------------------------------------
11201 // Loop over the vertices defining holes and mark those as outside the
11202 // outer polygons
11203 for (unsigned h = 0; h < n_input_holes; h++)
11204 {
11205 // Check if the vertex has not been already marked as inside
11206 // another polygon
11207 if (!is_inside_an_inner_polygon[h])
11208 {
11209 // Loop over the polygons and check if the vertex is outside ALL
11210 // the outer polygons
11211 for (unsigned i = 0; i < n_polygons; i++)
11212 {
11213 // Only work with outer polygons
11214 if (is_outer_polygon[i])
11215 {
11216 // Check if the hole is inside the current outer polygon
11217 const bool is_inside_polygon = is_point_inside_polygon_helper(
11218 vertices_polygons[i], output_holes_coordinates[h]);
11219
11220 // If the vertex is inside the current outer polygon then
11221 // mark it and break the loop (it is not outside ALL the
11222 // polygons)
11223 if (is_inside_polygon)
11224 {
11225 // Set as inside an outer polygon
11226 is_outside_the_outer_polygons[h] = false;
11227 // Break the loop
11228 break;
11229 } // if (is_inside_polygon)
11230
11231 } // if (is_outer_polygon[i])
11232
11233 } // for (i < n_polygons)
11234
11235 } // if (!is_inside_an_inner_polygon[h])
11236 else
11237 {
11238 // If the vertex is inside an inner polygon then it is inside an
11239 // outer polygon
11240 is_outside_the_outer_polygons[h] = false;
11241 } // else if (!is_inside_an_inner_polygon[h])
11242
11243 } // for (h < n_input_holes)
11244
11245 // -------------------------------------------------------------------
11246 // Compute the convex hull Create the data structure
11247 std::vector<Point> input_vertices_convex_hull;
11248 // Copy ALL the vertices of the polygons
11249 // Loop over the polygons
11250 for (unsigned p = 0; p < n_polygons; p++)
11251 {
11252 // Get the number of vertices
11253 const unsigned n_vertices = vertices_polygons[p].size();
11254 // Loop over the vertices in the polygon
11255 for (unsigned v = 0; v < n_vertices; v++)
11256 {
11257 // Create a new "Point" to store in the input vertices
11258 Point point;
11259 // Assign the values to the "Point"
11260 point.x = vertices_polygons[p][v][0];
11261 point.y = vertices_polygons[p][v][1];
11262 // Add the "Point" to the input vertices
11263 input_vertices_convex_hull.push_back(point);
11264 } // for (v < n_vertices)
11265 } // for (p < n_polygons)
11266
11267 // Compute the convex hull
11268 std::vector<Point> output_vertices_convex_hull =
11269 convex_hull(input_vertices_convex_hull);
11270
11271 // Get the number of vertices in the convex hull
11272 const unsigned n_vertices_convex_hull = output_vertices_convex_hull.size();
11273
11274 // Copy the output to the used data structures
11275 Vector<Vector<double>> vertices_convex_hull(n_vertices_convex_hull);
11276 for (unsigned i = 0; i < n_vertices_convex_hull; i++)
11277 {
11278 // Resize the data structure
11279 vertices_convex_hull[i].resize(2);
11280 // Copy the data
11281 vertices_convex_hull[i][0] = output_vertices_convex_hull[i].x;
11282 vertices_convex_hull[i][1] = output_vertices_convex_hull[i].y;
11283 } // for (i < n_vertices_convex_hull)
11284
11285 // Loop over the vertices defining holes, work only with those
11286 // outside ALL the outer boundaries and mark those inside the convex
11287 // hull
11288 for (unsigned h = 0; h < n_input_holes; h++)
11289 {
11290 // Only work with those outside ALL the outer polygons
11291 if (is_outside_the_outer_polygons[h])
11292 {
11293 // Check if the hole is inside the convex hull
11294 const bool is_inside_convex_hull = is_point_inside_polygon_helper(
11295 vertices_convex_hull, output_holes_coordinates[h]);
11296
11297 // If the vertex is inside the convex hull then mark it
11298 if (is_inside_convex_hull)
11299 {
11300 // Set as inside the convex hull
11301 is_inside_the_convex_hull[h] = true;
11302 } // if (is_inside_convex_hull)
11303
11304 } // if (is_outside_the_outer_polygons[h])
11305 else
11306 {
11307 // Any vertex inside any outer polygon is inside the convex hull
11308 is_inside_the_convex_hull[h] = true;
11309 } // else if (is_outside_the_outer_polygons[h])
11310
11311 } // for (h < n_input_holes)
11312
11313 // Store the output holes, only (those inside an inner polygon) OR
11314 // (those outside ALL the polygons AND inside the convex hull)
11315 Vector<Vector<double>> hole_kept;
11316 for (unsigned h = 0; h < n_input_holes; h++)
11317 {
11318 // Check if the hole should be kept
11319 if ((is_inside_an_inner_polygon[h]) ||
11320 (is_outside_the_outer_polygons[h] && is_inside_the_convex_hull[h]))
11321 {
11322 // Copy the hole information
11323 hole_kept.push_back(output_holes_coordinates[h]);
11324 } // if (keep_hole[h])
11325 } // for (h < n_input_holes)
11326
11327 // Clear the previous storage
11328 output_holes_coordinates.clear();
11329 // Set the output holes
11330 output_holes_coordinates = hole_kept;
11331 }
11332
11333 //======================================================================
11334 // Sorts the polylines so they be contiguous and then we can
11335 // create a closed or open curve from them
11336 //======================================================================
11337 template<class ELEMENT>
11339 Vector<TriangleMeshPolyLine*>& unsorted_polylines_pt,
11340 Vector<Vector<TriangleMeshPolyLine*>>& sorted_polylines_pt)
11341 {
11342 unsigned n_unsorted_polylines = unsorted_polylines_pt.size();
11343 unsigned n_sorted_polylines = 0;
11344 unsigned curves_index = 0;
11345
11346 // Map to know which polyline has been already sorted
11347 std::map<TriangleMeshPolyLine*, bool> done_polyline;
11348
11349 do
11350 {
11351 // Create the list that stores the polylines and allows to introduce
11352 // polylines to the left and to the right
11353 std::list<TriangleMeshPolyLine*> sorted_polyline_list_pt;
11354 bool changes = false;
11355
11356 // Create pointers to the left and right "side" of the sorted list of
11357 // new created TriangleMeshPolyLines
11358 TriangleMeshPolyLine* left_pt = 0;
11359 TriangleMeshPolyLine* right_pt = 0;
11360
11361 // 1) Take the first non done polyline on the unsorted list of polylines
11362 unsigned pp = 0;
11363 bool found_root_polyline = false;
11364 while (pp < n_unsorted_polylines && !found_root_polyline)
11365 {
11366 if (!done_polyline[unsorted_polylines_pt[pp]])
11367 {
11368 found_root_polyline = true;
11369 }
11370 else
11371 {
11372 pp++;
11373 }
11374 }
11375
11376 // Check if there are polylines to be sorted
11377 if (pp < n_unsorted_polylines)
11378 {
11379 // 2) Mark the polyline as done
11380 left_pt = right_pt = unsorted_polylines_pt[pp];
11381 done_polyline[left_pt] = true;
11382 // Increment the number of sorted polylines
11383 n_sorted_polylines++;
11384
11385 // 3) Add this polyline to the sorted list and use it as root
11386 // to sort the other polylines
11387 sorted_polyline_list_pt.push_back(left_pt);
11388
11389 do
11390 {
11391 changes = false;
11392
11393 Vector<double> left_vertex(2);
11394 Vector<double> right_vertex(2);
11395
11396 left_pt->initial_vertex_coordinate(left_vertex);
11397 right_pt->final_vertex_coordinate(right_vertex);
11398
11399 for (unsigned i = pp + 1; i < n_unsorted_polylines; i++)
11400 {
11401 TriangleMeshPolyLine* current_polyline_pt =
11402 unsorted_polylines_pt[i];
11403 if (!done_polyline[current_polyline_pt])
11404 {
11405 Vector<double> initial_vertex(2);
11406 Vector<double> final_vertex(2);
11407 current_polyline_pt->initial_vertex_coordinate(initial_vertex);
11408 current_polyline_pt->final_vertex_coordinate(final_vertex);
11409
11410 // Compare if the current polyline should go to the left or
11411 // to the right on the sorted polyline list
11412
11413 // Go to the left
11414 if (left_vertex == final_vertex)
11415 {
11416 left_pt = current_polyline_pt;
11417 sorted_polyline_list_pt.push_front(left_pt);
11418 done_polyline[left_pt] = true;
11419 n_sorted_polylines++;
11420
11421 // We have added one more polyline, go for another round
11422 changes = true;
11423 }
11424 // Go to the right
11425 else if (right_vertex == initial_vertex)
11426 {
11427 right_pt = current_polyline_pt;
11428 sorted_polyline_list_pt.push_back(right_pt);
11429 done_polyline[right_pt] = true;
11430 n_sorted_polylines++;
11431
11432 // We have added one more polyline, go for another round
11433 changes = true;
11434 }
11435 // Go to the left but it is reversed
11436 else if (left_vertex == initial_vertex)
11437 {
11438 current_polyline_pt->reverse();
11439 left_pt = current_polyline_pt;
11440 sorted_polyline_list_pt.push_front(left_pt);
11441 done_polyline[left_pt] = true;
11442 n_sorted_polylines++;
11443
11444 // We have added one more polyline, go for another round
11445 changes = true;
11446 }
11447 // Go to the right but it is reversed
11448 else if (right_vertex == final_vertex)
11449 {
11450 current_polyline_pt->reverse();
11451 right_pt = current_polyline_pt;
11452 sorted_polyline_list_pt.push_back(right_pt);
11453 done_polyline[right_pt] = true;
11454 n_sorted_polylines++;
11455
11456 // We have added one more polyline, go for another round
11457 changes = true;
11458 }
11459 } // if (!done_polyline[current_polyline_pt])
11460 if (changes)
11461 {
11462 break;
11463 }
11464 } // for (i < n_unsorted_polylines)
11465 } while (changes);
11466
11467 } // if (pp < n_unsorted_polylines)
11468 else
11469 {
11470 // All the polylines are now on the sorted list of polylines
11471#ifdef PARANOID
11472 // This case comes when it was not possible to find a root polyline
11473 // since all of them are marked as done but the number of sorted and
11474 // unsorted polylines is not the same
11475 if (!found_root_polyline)
11476 {
11477 std::stringstream err;
11478 err << "It was not possible to find a root polyline to sort the "
11479 << "others around it.\nThe number of unsorted and sorted "
11480 << "polylines is different, it means that\nnot all the "
11481 << "polylines have been sorted.\n"
11482 << "Found root polyline: (" << found_root_polyline << ")\n"
11483 << "Sorted polylines: (" << n_sorted_polylines << ")\n"
11484 << "Unsorted polylines: (" << n_unsorted_polylines << ")\n";
11485 throw OomphLibError(err.str(),
11486 "TriangleMesh::sort_polylines_helper()",
11487 OOMPH_EXCEPTION_LOCATION);
11488 }
11489#endif
11490 }
11491
11492 // Create the storage for the new sorted polylines and copy them on the
11493 // vector structure for sorted polylines
11494 unsigned n_sorted_polyline_on_list = sorted_polyline_list_pt.size();
11495
11496 // Create the temporal vector that stores the sorted polylines
11497 Vector<TriangleMeshPolyLine*> tmp_sorted_polylines(
11498 n_sorted_polyline_on_list);
11499 unsigned counter = 0;
11500
11501 std::list<TriangleMeshPolyLine*>::iterator it_polyline;
11502 for (it_polyline = sorted_polyline_list_pt.begin();
11503 it_polyline != sorted_polyline_list_pt.end();
11504 it_polyline++)
11505 {
11506 tmp_sorted_polylines[counter] = *it_polyline;
11507 counter++;
11508 }
11509
11510 sorted_polylines_pt.push_back(tmp_sorted_polylines);
11511
11512 ++curves_index;
11513
11514 } while (n_sorted_polylines < n_unsorted_polylines);
11515
11516#ifdef PARANOID
11517 // Verify that the number of polylines on the sorted list is the same
11518 // as the number of polylines on the unsorted list
11519 if (n_sorted_polylines != n_unsorted_polylines)
11520 {
11521 std::stringstream err;
11522 err << "The number of polylines on the unsorted and sorted vectors"
11523 << " is different,\n"
11524 << "it means that not all the polylines have been sorted.\n"
11525 << "Sorted polylines: " << n_sorted_polylines
11526 << "\nUnsorted polylines: " << n_unsorted_polylines;
11527 throw OomphLibError(err.str(),
11528 "TriangleMesh::sort_polylines_helper()",
11529 OOMPH_EXCEPTION_LOCATION);
11530 }
11531#endif
11532 }
11533
11534 //======================================================================
11535 // Creates the shared boundaries
11536 //======================================================================
11537 template<class ELEMENT>
11539 OomphCommunicator* comm_pt,
11540 const Vector<unsigned>& element_domain,
11541 const Vector<GeneralisedElement*>& backed_up_el_pt,
11542 const Vector<FiniteElement*>& backed_up_f_el_pt,
11543 std::map<Data*, std::set<unsigned>>& processors_associated_with_data,
11544 const bool& overrule_keep_as_halo_element_status)
11545 {
11546 // Storage for number of processors and current processor
11547 const unsigned nproc = comm_pt->nproc();
11548 const unsigned my_rank = comm_pt->my_rank();
11549
11550 // Storage for all the halo elements on all processors
11551 // halo_element[iproc][jproc][ele_number]
11552 // Stores the "ele_number"-th halo element of processor "iproc" with
11553 // processor "jproc"
11554 Vector<Vector<Vector<GeneralisedElement*>>> halo_element_pt(nproc);
11555 // Create complete storage for the halo_element_pt container
11556 for (unsigned iproc = 0; iproc < nproc; iproc++)
11557 {
11558 halo_element_pt[iproc].resize(nproc);
11559 }
11560
11561 // Store the global index of the element, used to check for possible
11562 // misclassification of halo elements in the above container
11563 // (halo_element_pt)
11564 std::map<GeneralisedElement*, unsigned> element_to_global_index;
11565
11566 // Get the halo elements on all processors
11567 this->get_halo_elements_on_all_procs(nproc,
11568 element_domain,
11569 backed_up_el_pt,
11570 processors_associated_with_data,
11571 overrule_keep_as_halo_element_status,
11572 element_to_global_index,
11573 halo_element_pt);
11574
11575 // Resize the shared polylines container
11576 flush_shared_boundary_polyline_pt();
11577 Shared_boundary_polyline_pt.resize(nproc);
11578
11579 // Create a set that store only the elements that will be kept in
11580 // the processor as nonhalo element, those whose element_domains is
11581 // equal to my_rank. This set is used when creating the shared
11582 // polylines and identify the connections to the original boundaries
11583 std::set<FiniteElement*> element_in_processor_pt;
11584 const unsigned n_ele = backed_up_f_el_pt.size();
11585 for (unsigned e = 0; e < n_ele; e++)
11586 {
11587 if (element_domain[e] == my_rank)
11588 {
11589 element_in_processor_pt.insert(backed_up_f_el_pt[e]);
11590 } // if (element_domain[e] == my_rank)
11591 } // for (e < n_elex)
11592
11593 // Look for elements edges that may lie on internal boundaries
11594 // If that is the case then relate the face with the boundary on
11595 // which it lies
11596 std::map<std::pair<Node*, Node*>, unsigned> elements_edges_on_boundary;
11597 this->get_element_edges_on_boundary(elements_edges_on_boundary);
11598
11599 // Now we have all the halo elements on all processors. Use the
11600 // edges shared by the halo elements to create the shared boundaries.
11601 this->create_polylines_from_halo_elements_helper(
11602 element_domain,
11603 element_to_global_index,
11604 element_in_processor_pt,
11605 halo_element_pt,
11606 elements_edges_on_boundary,
11607 Shared_boundary_polyline_pt);
11608 }
11609
11610 //======================================================================
11611 /// Creates the halo elements on all processors
11612 /// Gets the halo elements on all processors, these elements are then used
11613 /// on the function that computes the shared boundaries among the processors
11614 //======================================================================
11615 template<class ELEMENT>
11617 const unsigned& nproc,
11618 const Vector<unsigned>& element_domain,
11619 const Vector<GeneralisedElement*>& backed_up_el_pt,
11620 std::map<Data*, std::set<unsigned>>& processors_associated_with_data,
11621 const bool& overrule_keep_as_halo_element_status,
11622 std::map<GeneralisedElement*, unsigned>& element_to_global_index,
11623 Vector<Vector<Vector<GeneralisedElement*>>>& output_halo_elements_pt)
11624 {
11625 const unsigned n_ele = backed_up_el_pt.size();
11626
11627 // Loop over all the processors
11628 for (unsigned iproc = 0; iproc < nproc; iproc++)
11629 {
11630 // Boolean to know which elements has been already added to the
11631 // halo scheme on "iproc" processor
11632 Vector<std::map<GeneralisedElement*, bool>> already_added(nproc);
11633
11634 // Loop over all backed up elements
11635 for (unsigned e = 0; e < n_ele; e++)
11636 {
11637 // Get element and its domain
11638 GeneralisedElement* el_pt = backed_up_el_pt[e];
11639 unsigned el_domain = element_domain[e];
11640
11641 // If element is NOT located on "iproc" processor then check if it is
11642 // halo with "el_domain" processor
11643 if (el_domain != iproc)
11644 {
11645 // If this current mesh has been told to keep all elements as halos,
11646 // OR the element itself knows that it must be kept then
11647 // keep it
11648 if ((this->Keep_all_elements_as_halos) ||
11649 (el_pt->must_be_kept_as_halo()))
11650 {
11651 if (!overrule_keep_as_halo_element_status)
11652 {
11653 // Add as halo element whose non-halo counterpart is
11654 // located on processor "el_domain"
11655 if (!already_added[el_domain][el_pt])
11656 {
11657 output_halo_elements_pt[iproc][el_domain].push_back(el_pt);
11658 already_added[el_domain][el_pt] = true;
11659 element_to_global_index[el_pt] = e;
11660 }
11661 }
11662 }
11663 // Otherwise: Is one of the nodes associated with other processor?
11664 else
11665 {
11666 // Can only have nodes if this is a finite element
11667 FiniteElement* finite_el_pt = dynamic_cast<FiniteElement*>(el_pt);
11668 if (finite_el_pt != 0)
11669 {
11670 unsigned n_node = finite_el_pt->nnode();
11671 for (unsigned n = 0; n < n_node; n++)
11672 {
11673 Node* nod_pt = finite_el_pt->node_pt(n);
11674
11675 // Keep element?
11676 std::set<unsigned>::iterator it =
11677 processors_associated_with_data[nod_pt].find(iproc);
11678 if (it != processors_associated_with_data[nod_pt].end())
11679 {
11680 // Add as root halo element whose non-halo counterpart is
11681 // located on processor "el_domain"
11682 if (!already_added[el_domain][el_pt])
11683 {
11684 output_halo_elements_pt[iproc][el_domain].push_back(el_pt);
11685 already_added[el_domain][el_pt] = true;
11686 element_to_global_index[el_pt] = e;
11687 }
11688 // Now break out of loop over nodes
11689 break;
11690 } // if (it!=processors_associated_with_data[nod_pt].end())
11691 } // for (n < n_node)
11692 } // if (finite_el_pt!=0)
11693 } // else (this->Keep_all_elements_as_halos)
11694 } // if (el_domain!=iproc)
11695 } // for (e < nele)
11696 } // for (iproc < nproc)
11697 }
11698
11699 //====================================================================
11700 // Get the element edges (pair of nodes, edges) that lie
11701 // on a boundary (used to mark shared boundaries that lie on
11702 // internal boundaries)
11703 //====================================================================
11704 template<class ELEMENT>
11706 std::map<std::pair<Node*, Node*>, unsigned>& element_edges_on_boundary)
11707 {
11708 // The number of original boundaries
11709 const unsigned nbound = this->nboundary();
11710 // Loop over the boundaries
11711 for (unsigned b = 0; b < nbound; b++)
11712 {
11713 // Keep track of the pair of nodes done
11714 std::map<std::pair<Node*, Node*>, bool> edge_done;
11715 // Get the number of elements on the boundary
11716 const unsigned nbound_ele = this->nboundary_element(b);
11717 for (unsigned e = 0; e < nbound_ele; e++)
11718 {
11719 // Get the boundary bulk element
11720 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
11721 // Get the face index
11722 int face_index = this->face_index_at_boundary(b, e);
11723 // Create the face element
11724 FiniteElement* face_ele_pt =
11725 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
11726 // Get the number of nodes on the face element
11727 const unsigned nnodes = face_ele_pt->nnode();
11728 // Get the first and last node
11729 Node* first_node_pt = face_ele_pt->node_pt(0);
11730 Node* last_node_pt = face_ele_pt->node_pt(nnodes - 1);
11731
11732 // Create the pair to store the nodes
11733 std::pair<Node*, Node*> edge =
11734 std::make_pair(first_node_pt, last_node_pt);
11735
11736 // Has the edge been included
11737 if (!edge_done[edge])
11738 {
11739 // Mark the edge as done
11740 edge_done[edge] = true;
11741
11742 // Create the reversed version and mark it as done too
11743 std::pair<Node*, Node*> inv_edge =
11744 std::make_pair(last_node_pt, first_node_pt);
11745
11746 // Mark the reversed edge as done
11747 edge_done[inv_edge] = true;
11748
11749 // Mark the edge to belong to boundary b
11750 element_edges_on_boundary[edge] = b;
11751 } // if (!edge_done[edge])
11752
11753 // Free the memory allocated for the face element
11754 delete face_ele_pt;
11755 face_ele_pt = 0;
11756
11757 } // for (e < nbound_ele)
11758
11759 } // for (b < nbound)
11760 }
11761
11762 // ======================================================================
11763 // Creates polylines from the intersection of halo elements on
11764 // all processors. The new polylines define the shared boundaries in
11765 // the domain This method computes the polylines on ALL processors,
11766 // that is why the three dimensions in the structure
11767 // output_polylines_pt[iproc][ncurve][npolyline]
11768 // ======================================================================
11769 template<class ELEMENT>
11771 const Vector<unsigned>& element_domain,
11772 std::map<GeneralisedElement*, unsigned>& element_to_global_index,
11773 std::set<FiniteElement*>& element_in_processor_pt,
11774 Vector<Vector<Vector<GeneralisedElement*>>>& input_halo_elements,
11775 std::map<std::pair<Node*, Node*>, unsigned>& elements_edges_on_boundary,
11776 Vector<Vector<Vector<TriangleMeshPolyLine*>>>& output_polylines_pt)
11777 {
11778 const unsigned nproc = this->communicator_pt()->nproc();
11779 const unsigned my_rank = this->communicator_pt()->my_rank();
11780
11781 // ---------------------------------------------------------------
11782 // Get the edges shared between each pair of processors
11783 // ---------------------------------------------------------------
11784
11785 // Storage for the edges (pair of nodes) shared between a pair of
11786 // processors
11788
11789 // Each edge is associated to two elements, a haloi (halo element
11790 // in processors i) and a haloj (halo element in processors j)
11791 Vector<Vector<Vector<Vector<FiniteElement*>>>> edge_element_pt(nproc);
11792
11793 // Each edge is associated to two elements, a haloi and a haloj,
11794 // the edge was created from a given face from each element, the
11795 // haloi face is stored at [0], the haloj face is stored at [1]
11796 Vector<Vector<Vector<Vector<int>>>> edge_element_face(nproc);
11797
11798 // Store the possible internal boundary id associated to each edge
11799 // (-1 if there is no association). Some edges may overlap an
11800 // internal boundary (and only internal boundaries)
11801 Vector<Vector<Vector<int>>> edge_boundary(nproc);
11802
11803 // Mark those edges (pair of nodes overlapped by a shared boundary)
11804 std::map<std::pair<Node*, Node*>, bool> overlapped_edge;
11805
11806 // Resize the containers, they store info. for each pair of
11807 // processors
11808
11809 // First resize the global container
11810 Shared_boundaries_ids.resize(nproc);
11811 for (unsigned j = 0; j < nproc; j++)
11812 {
11813 edges[j].resize(nproc);
11814 edge_element_pt[j].resize(nproc);
11815 edge_element_face[j].resize(nproc);
11816 edge_boundary[j].resize(nproc);
11817
11818 // Resize the global container for shared boundaries ids
11819 Shared_boundaries_ids[j].resize(nproc);
11820
11821 } // for (j < nproc)
11822
11823 // Take the halo elements of processor "iproc" and compare their
11824 // edges with halo elements of other processors (except itself)
11825 for (unsigned iproc = 0; iproc < nproc; iproc++)
11826 {
11827 // Take the halo elements of processor iproc and compare with
11828 // other processors
11829 // Start from the iproc + 1,
11830 // 1) To avoid comparing with itself,
11831 // 2) To avoid generation of repeated boundaries
11832 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
11833 {
11834 // **************************************************************
11835 // FIRST PART
11836 // 1) Get the halo elements of processor "iproc" with processor
11837 // "jproc"
11838 // 2) Get the halo elements of processor "jproc" with processor
11839 // "iproc"
11840 // 3) Compare their edges and those that match are the ones that
11841 // define the shared boundaries
11842 // **************************************************************
11843
11844 // Storage for halo elements
11845 Vector<GeneralisedElement*> halo_elements_iproc_with_jproc;
11846 Vector<GeneralisedElement*> halo_elements_jproc_with_iproc;
11847
11848 // Get the halo elements of "iproc" with "jproc"
11849 halo_elements_iproc_with_jproc = input_halo_elements[iproc][jproc];
11850
11851 // If there are halo elements then there are shared boundaries
11852 const unsigned nhalo_elements_iproc_with_jproc =
11853 halo_elements_iproc_with_jproc.size();
11854 // DEBP(nhalo_elements_iproc_with_jproc);
11855 if (nhalo_elements_iproc_with_jproc > 0)
11856 {
11857 // Get the halo elements of "jproc" with "iproc"
11858 halo_elements_jproc_with_iproc = input_halo_elements[jproc][iproc];
11859
11860 // If there are halo elements then there are shared
11861 // boundaries
11862 const unsigned nhalo_elements_jproc_with_iproc =
11863 halo_elements_jproc_with_iproc.size();
11864// DEBP(nhalo_elements_jproc_with_iproc);
11865#ifdef PARANOID
11866 if (nhalo_elements_jproc_with_iproc == 0)
11867 {
11868 // If there are halo elements of iproc with jproc there
11869 // MUST be halo elements on the other way round, not
11870 // necessary the same but at least one
11871 std::stringstream err;
11872 err << "There are no halo elements from processor (" << jproc
11873 << ") "
11874 << "with processor (" << iproc << ").\n"
11875 << "This is strange since there are halo elements from "
11876 << "processor (" << iproc << ") with processor (" << jproc
11877 << ").\n"
11878 << "Number of halo elements from (" << iproc << ") to ("
11879 << jproc << ") : (" << nhalo_elements_iproc_with_jproc << ")\n"
11880 << "Number of halo elements from (" << jproc << ") to ("
11881 << iproc << ") : (" << nhalo_elements_jproc_with_iproc << ")\n";
11882 throw OomphLibError(
11883 err.str(),
11884 "TriangleMesh::create_polylines_from_halo_elements_helper()",
11885 OOMPH_EXCEPTION_LOCATION);
11886 }
11887#endif
11888 // The edges are defined as pair of nodes
11889 Vector<Node*> halo_edges_iproc;
11890 unsigned halo_edges_counter_iproc = 0;
11891 Vector<Node*> halo_edges_jproc;
11892 unsigned halo_edges_counter_jproc = 0;
11893
11894 // Map to associate the edge with the element used to create it
11895 std::map<std::pair<Node*, Node*>, FiniteElement*>
11896 edgesi_to_element_pt;
11897
11898 // Map to associated the edge with the face number of the
11899 // element that created it
11900 std::map<std::pair<std::pair<Node*, Node*>, FiniteElement*>, int>
11901 edgesi_element_pt_to_face_index;
11902
11903 // Map to associate the edge with the element used to create it
11904 std::map<std::pair<Node*, Node*>, FiniteElement*>
11905 edgesj_to_element_pt;
11906
11907 // Map to associated the edge with the face number of the
11908 // element that created it
11909 std::map<std::pair<std::pair<Node*, Node*>, FiniteElement*>, int>
11910 edgesj_element_pt_to_face_index;
11911
11912 // **************************************************************
11913 // 1.1) Store the edges of the "iproc" halo elements
11914 // **************************************************************
11915 // Go throught halo elements on "iproc" processor
11916 for (unsigned ih = 0; ih < nhalo_elements_iproc_with_jproc; ih++)
11917 {
11918#ifdef PARANOID
11919 unsigned e =
11920 element_to_global_index[halo_elements_iproc_with_jproc[ih]];
11921 // Only work with halo elements inside the "jproc" processor
11922 if (element_domain[e] != jproc)
11923 {
11924 // There was a problem on the ihalo-jhalo classification
11925 std::stringstream err;
11926 err << "There was a problem on the ihalo-jhalo classification.\n"
11927 << "One of the elements, (the one with the (" << e << ")-th "
11928 << "index ) is not on the (" << jproc << ")-th processor\n"
11929 << "but it was stored as a halo element of processor ("
11930 << iproc << ") with processor (" << jproc << ").\n";
11931 throw OomphLibError(
11932 err.str(),
11933 "TriangleMesh::create_polylines_from_halo_elements_helper()",
11934 OOMPH_EXCEPTION_LOCATION);
11935 }
11936#endif
11937
11938 FiniteElement* el_pt =
11939 dynamic_cast<FiniteElement*>(halo_elements_iproc_with_jproc[ih]);
11940
11941 if (el_pt == 0)
11942 {
11943 std::stringstream err;
11944 err << "The halo element (" << ih
11945 << ") could not be casted to the "
11946 << "FiniteElement type.\n";
11947 throw OomphLibError(
11948 err.str(),
11949 "TriangleMesh::create_polylines_from_halo_elements_helper()",
11950 OOMPH_EXCEPTION_LOCATION);
11951 }
11952
11953#ifdef PARANOID
11954 // Number of nodes on this element
11955 const unsigned n_nodes = el_pt->nnode();
11956
11957 // The number of nodes on every element should be at least
11958 // three since we are going to work with the cornes nodes,
11959 // the ones with index 0, 1 and 2
11960 if (n_nodes < 3)
11961 {
11962 std::stringstream err;
11963 err << "The number of nodes of the " << ih
11964 << "-th halo element is"
11965 << " (" << n_nodes << ").\nWe can not work with triangle "
11966 << "elements with less than three nodes\n";
11967 throw OomphLibError(
11968 err.str(),
11969 "TriangleMesh::create_polylines_from_halo_elements_helper()",
11970 OOMPH_EXCEPTION_LOCATION);
11971 }
11972#endif
11973
11974 // Get the corner nodes, the first three nodes
11975 Node* first_node_pt = el_pt->node_pt(0);
11976 Node* second_node_pt = el_pt->node_pt(1);
11977 Node* third_node_pt = el_pt->node_pt(2);
11978
11979 // Store the edges
11980 halo_edges_iproc.push_back(first_node_pt);
11981 halo_edges_iproc.push_back(second_node_pt);
11982 halo_edges_counter_jproc++;
11983
11984 halo_edges_iproc.push_back(second_node_pt);
11985 halo_edges_iproc.push_back(third_node_pt);
11986 halo_edges_counter_jproc++;
11987
11988 halo_edges_iproc.push_back(third_node_pt);
11989 halo_edges_iproc.push_back(first_node_pt);
11990 halo_edges_counter_jproc++;
11991
11992 // Store the info. of the element used to create these edges
11993 std::pair<Node*, Node*> edge1 =
11994 std::make_pair(first_node_pt, second_node_pt);
11995 edgesi_to_element_pt[edge1] = el_pt;
11996
11997 std::pair<Node*, Node*> edge2 =
11998 std::make_pair(second_node_pt, third_node_pt);
11999 edgesi_to_element_pt[edge2] = el_pt;
12000
12001 std::pair<Node*, Node*> edge3 =
12002 std::make_pair(third_node_pt, first_node_pt);
12003 edgesi_to_element_pt[edge3] = el_pt;
12004
12005 // Store the face index of the edge in the element
12006 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele1 =
12007 std::make_pair(edge1, el_pt);
12008 edgesi_element_pt_to_face_index[edge_ele1] = 2;
12009
12010 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele2 =
12011 std::make_pair(edge2, el_pt);
12012 edgesi_element_pt_to_face_index[edge_ele2] = 0;
12013
12014 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele3 =
12015 std::make_pair(edge3, el_pt);
12016 edgesi_element_pt_to_face_index[edge_ele3] = 1;
12017
12018 } // for (ih < nhalo_elements_iproc_with_jproc)
12019
12020 // **************************************************************
12021 // 1.2) Store the edges of the "jproc" halo elements
12022 // **************************************************************
12023 // Go throught halo elements on "jproc" processor
12024 for (unsigned jh = 0; jh < nhalo_elements_jproc_with_iproc; jh++)
12025 {
12026#ifdef PARANOID
12027 unsigned e =
12028 element_to_global_index[halo_elements_jproc_with_iproc[jh]];
12029 // Only work with halo elements inside the "jproc" processor
12030 if (element_domain[e] != iproc)
12031 {
12032 // There was a problem on the jhalo-ihalo classification
12033 std::stringstream err;
12034 err << "There was a problem on the jhalo-ihalo classification.\n"
12035 << "One of the elements, (the one with the (" << e << ")-th "
12036 << "index ) is not on the (" << iproc << ")-th processor\n"
12037 << "but it was stored as a halo element of processor ("
12038 << jproc << ") with processor (" << iproc << ").\n";
12039 throw OomphLibError(
12040 err.str(),
12041 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12042 OOMPH_EXCEPTION_LOCATION);
12043 }
12044#endif
12045
12046 FiniteElement* el_pt =
12047 dynamic_cast<FiniteElement*>(halo_elements_jproc_with_iproc[jh]);
12048 if (el_pt == 0)
12049 {
12050 std::stringstream err;
12051 err << "The halo element (" << jh
12052 << ") could not be casted to the "
12053 << "FiniteElement type.\n";
12054 throw OomphLibError(
12055 err.str(),
12056 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12057 OOMPH_EXCEPTION_LOCATION);
12058 }
12059
12060#ifdef PARANOID
12061 // Number of nodes on this element
12062 const unsigned n_nodes = el_pt->nnode();
12063
12064 // The number of nodes on every element should be at least
12065 // three since we are going to work with the cornes nodes,
12066 // the ones with index 0, 1 and 2
12067 if (n_nodes < 3)
12068 {
12069 std::stringstream err;
12070 err << "The number of nodes of the " << jh
12071 << "-th halo element is"
12072 << " (" << n_nodes << ").\nWe can not work with triangle "
12073 << "elements with less than three nodes\n";
12074 throw OomphLibError(
12075 err.str(),
12076 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12077 OOMPH_EXCEPTION_LOCATION);
12078 }
12079#endif
12080
12081 // Get the nodes pointers
12082 Node* first_node_pt = el_pt->node_pt(0);
12083 Node* second_node_pt = el_pt->node_pt(1);
12084 Node* third_node_pt = el_pt->node_pt(2);
12085
12086 // Store the edges
12087 halo_edges_jproc.push_back(first_node_pt);
12088 halo_edges_jproc.push_back(second_node_pt);
12089 halo_edges_counter_iproc++;
12090
12091 halo_edges_jproc.push_back(second_node_pt);
12092 halo_edges_jproc.push_back(third_node_pt);
12093 halo_edges_counter_iproc++;
12094
12095 halo_edges_jproc.push_back(third_node_pt);
12096 halo_edges_jproc.push_back(first_node_pt);
12097 halo_edges_counter_iproc++;
12098
12099 // Store the info. of the element used to create these edges
12100 std::pair<Node*, Node*> edge1 =
12101 std::make_pair(first_node_pt, second_node_pt);
12102 edgesj_to_element_pt[edge1] = el_pt;
12103
12104 std::pair<Node*, Node*> edge2 =
12105 std::make_pair(second_node_pt, third_node_pt);
12106 edgesj_to_element_pt[edge2] = el_pt;
12107
12108 std::pair<Node*, Node*> edge3 =
12109 std::make_pair(third_node_pt, first_node_pt);
12110 edgesj_to_element_pt[edge3] = el_pt;
12111
12112 // Store the face index of the edge in the element
12113 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele1 =
12114 std::make_pair(edge1, el_pt);
12115 edgesj_element_pt_to_face_index[edge_ele1] = 2;
12116
12117 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele2 =
12118 std::make_pair(edge2, el_pt);
12119 edgesj_element_pt_to_face_index[edge_ele2] = 0;
12120
12121 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele3 =
12122 std::make_pair(edge3, el_pt);
12123 edgesj_element_pt_to_face_index[edge_ele3] = 1;
12124
12125 } // for (jh < nhalo_elements_jproc_with_iproc)
12126
12127 // ***************************************************************
12128 // SECOND PART
12129 // 1) We already have the information of the edges on the iproc
12130 // halo and jproc halo elements
12131 // 2) Identify the shared edges to create the shared boundaries
12132 // (Only store the information but do not create the polyline)
12133 // ***************************************************************
12134
12135 // Get the number of edges from each processor
12136 unsigned nhalo_iedges = halo_edges_iproc.size();
12137 unsigned nhalo_jedges = halo_edges_jproc.size();
12138
12139 // Start comparing the edges to check which of those are
12140 // shared between the "ihalo_edge" and the "jhalo_edge"
12141 for (unsigned ihe = 0; ihe < nhalo_iedges; ihe += 2)
12142 {
12143 // Get the ihe-th edge (pair of nodes)
12144 Vector<Node*> ihalo_edge(2);
12145 ihalo_edge[0] = halo_edges_iproc[ihe];
12146 ihalo_edge[1] = halo_edges_iproc[ihe + 1];
12147
12148 // Create the pair that defines the edge
12149 std::pair<Node*, Node*> tmp_edge =
12150 std::make_pair(ihalo_edge[0], ihalo_edge[1]);
12151
12152 // Check if the edge lies on a boundary (default values is
12153 // -1 for no association with an internal boundary)
12154 int edge_boundary_id = -1;
12155 {
12156 std::map<std::pair<Node*, Node*>, unsigned>::iterator it;
12157 it = elements_edges_on_boundary.find(tmp_edge);
12158 // If the edges lie on a boundary then get the boundary id
12159 // on which the edges lie
12160 if (it != elements_edges_on_boundary.end())
12161 {
12162 // Assign the internal boundary id associated with the
12163 // edge
12164 edge_boundary_id = (*it).second;
12165 }
12166 else
12167 {
12168 // Look for the reversed version of the edge (the nodes
12169 // inverted)
12170 std::pair<Node*, Node*> rtmp_edge =
12171 std::make_pair(ihalo_edge[1], ihalo_edge[0]);
12172 it = elements_edges_on_boundary.find(rtmp_edge);
12173 if (it != elements_edges_on_boundary.end())
12174 {
12175 // Assign the internal boundary id associated with the
12176 // edge
12177 edge_boundary_id = (*it).second;
12178 }
12179 }
12180 }
12181
12182 // Go through the jhalo_edge and compare with the
12183 // ihalo_edge
12184 for (unsigned jhe = 0; jhe < nhalo_jedges; jhe += 2)
12185 {
12186 // Get the jhe-th edge (pair of nodes)
12187 Vector<Node*> jhalo_edge(2);
12188 jhalo_edge[0] = halo_edges_jproc[jhe];
12189 jhalo_edge[1] = halo_edges_jproc[jhe + 1];
12190
12191 // Comparing pointer of nodes
12192 if (ihalo_edge[0] == jhalo_edge[0] &&
12193 ihalo_edge[1] == jhalo_edge[1])
12194 {
12195 // Create the edge (both nodes that make the edge)
12196 std::pair<Node*, Node*> new_edge =
12197 std::make_pair(ihalo_edge[0], ihalo_edge[1]);
12198
12199 // Get the elements involved in the creation of the
12200 // edge to check that there are elements associated to
12201 // the edge
12202 FiniteElement* haloi_ele_pt = 0;
12203 haloi_ele_pt = edgesi_to_element_pt[new_edge];
12204 FiniteElement* haloj_ele_pt = 0;
12205 haloj_ele_pt = edgesj_to_element_pt[new_edge];
12206
12207 // Verify that there is an element associated with it
12208 if (haloi_ele_pt == 0 || haloj_ele_pt == 0)
12209 {
12210 std::stringstream err;
12211 err << "There is no associated elements with the new "
12212 << "shared boundary. This is an storing problem,\n"
12213 << "possibly related with a memory leak problem!!!\n"
12214 << "The nodes that compound the edge are these:\n"
12215 << "On processor (" << iproc << "):\n"
12216 << "(" << ihalo_edge[0]->x(0) << ", "
12217 << ihalo_edge[0]->x(1) << ") and (" << ihalo_edge[1]->x(0)
12218 << ", " << ihalo_edge[1]->x(1) << ")\n\n"
12219 << "On processor (" << jproc << "):\n"
12220 << "(" << jhalo_edge[0]->x(0) << ", "
12221 << jhalo_edge[0]->x(1) << ") and (" << jhalo_edge[1]->x(0)
12222 << ", " << jhalo_edge[1]->x(1) << ")\n\n"
12223 << "The nodes coordinates should be the same!!!\n";
12224 throw OomphLibError(err.str(),
12225 "TriangleMesh::create_polylines_from_"
12226 "halo_elements_helper()",
12227 OOMPH_EXCEPTION_LOCATION);
12228 }
12229
12230 // Store the edge
12231 edges[iproc][jproc].push_back(new_edge);
12232
12233 // Is the edge overlapped by a shared boundary
12234 if (edge_boundary_id >= 0)
12235 {
12236 // Mark the edge as overlapped
12237 overlapped_edge[new_edge] = true;
12238
12239 // Also mark the reversed edge
12240 std::pair<Node*, Node*> rev_new_edge =
12241 std::make_pair(ihalo_edge[1], ihalo_edge[0]);
12242
12243 // Mark the edge as overlapped
12244 overlapped_edge[rev_new_edge] = true;
12245
12246 } // if (edge_boundary_id >= 0)
12247
12248 // Store the internal boundary id (default -1)
12249 // associated to the edge
12250 edge_boundary[iproc][jproc].push_back(edge_boundary_id);
12251
12252 // Store the two elements associated with the edge
12253 Vector<FiniteElement*> tmp_elements_pt;
12254 tmp_elements_pt.push_back(haloi_ele_pt);
12255 tmp_elements_pt.push_back(haloj_ele_pt);
12256
12257 // Associate the edge with the elements that gave rise to it
12258 edge_element_pt[iproc][jproc].push_back(tmp_elements_pt);
12259
12260 // Get the face index on each element that gave rise to
12261 // the edge
12262
12263 // .. first create the pair (edge, finite_element)
12264 std::pair<std::pair<Node*, Node*>, FiniteElement*>
12265 edge_elementi_pair = make_pair(new_edge, haloi_ele_pt);
12266
12267 std::pair<std::pair<Node*, Node*>, FiniteElement*>
12268 edge_elementj_pair = make_pair(new_edge, haloj_ele_pt);
12269
12270 // Set default values to later check if values were
12271 // read from the map structure
12272 int face_index_haloi_ele = -1;
12273 face_index_haloi_ele =
12274 edgesi_element_pt_to_face_index[edge_elementi_pair];
12275 int face_index_haloj_ele = -1;
12276 face_index_haloj_ele =
12277 edgesj_element_pt_to_face_index[edge_elementj_pair];
12278 // Verify that there is an element associated with it
12279 if (face_index_haloi_ele == -1 || face_index_haloj_ele == -1)
12280 {
12281 std::stringstream err;
12282 err << "There is no associated face indexes to the"
12283 << "elements that gave\nrise to the shared edge\n"
12284 << "The nodes that compound the edge are these:\n"
12285 << "On processor (" << iproc << "):\n"
12286 << "(" << ihalo_edge[0]->x(0) << ", "
12287 << ihalo_edge[0]->x(1) << ") and (" << ihalo_edge[1]->x(0)
12288 << ", " << ihalo_edge[1]->x(1) << ")\n\n"
12289 << "On processor (" << jproc << "):\n"
12290 << "(" << jhalo_edge[0]->x(0) << ", "
12291 << jhalo_edge[0]->x(1) << ") and (" << jhalo_edge[1]->x(0)
12292 << ", " << jhalo_edge[1]->x(1) << ")\n\n"
12293 << "The nodes coordinates should be the same!!!\n";
12294 throw OomphLibError(err.str(),
12295 "TriangleMesh::create_polylines_from_"
12296 "halo_elements_helper()",
12297 OOMPH_EXCEPTION_LOCATION);
12298 } // if (face_index_haloi_ele == -1 ||
12299 // face_index_haloj_ele == -1)
12300
12301 // Get the face indexes from the map structure
12302 Vector<int> tmp_edge_element_face_index;
12303 tmp_edge_element_face_index.push_back(face_index_haloi_ele);
12304 tmp_edge_element_face_index.push_back(face_index_haloj_ele);
12305 // Store the face indexes
12306 edge_element_face[iproc][jproc].push_back(
12307 tmp_edge_element_face_index);
12308
12309 break; // break for (jhe < nhalo_jedges) since edge
12310 // found
12311
12312 } // if (ihalo_edge[0] == jhalo_edge[0] &&
12313 // ihalo_edge[1] == jhalo_edge[1])
12314 // Comparing nodes pointers
12315 else if (ihalo_edge[0] == jhalo_edge[1] &&
12316 ihalo_edge[1] == jhalo_edge[0])
12317 {
12318 // Create the edge (both nodes that make the edge)
12319 std::pair<Node*, Node*> new_edge =
12320 std::make_pair(ihalo_edge[0], ihalo_edge[1]);
12321
12322 // Get the elements involved in the creation of the
12323 // edge
12324 FiniteElement* haloi_ele_pt = 0;
12325 haloi_ele_pt = edgesi_to_element_pt[new_edge];
12326
12327 FiniteElement* haloj_ele_pt = 0;
12328 // Create the edge (reversed, that is how it was
12329 // originally stored)
12330 std::pair<Node*, Node*> new_edge_reversed =
12331 std::make_pair(jhalo_edge[0], jhalo_edge[1]);
12332 haloj_ele_pt = edgesj_to_element_pt[new_edge_reversed];
12333
12334 // Verify that there is an element associated with it
12335 if (haloi_ele_pt == 0 || haloj_ele_pt == 0)
12336 {
12337 std::stringstream err;
12338 err << "There is no associated elements with the new "
12339 << "shared boundary (reversed version). This is an "
12340 << "storing problem, possibly related with a memory "
12341 << "leak problem!!!\n"
12342 << "The nodes that compound the edge are these:\n"
12343 << "On processor (" << iproc << "):\n"
12344 << "(" << ihalo_edge[0]->x(0) << ", "
12345 << ihalo_edge[0]->x(1) << ") and (" << ihalo_edge[1]->x(0)
12346 << ", " << ihalo_edge[1]->x(1) << ")\n\n"
12347 << "On processor (" << jproc << "):\n"
12348 << "(" << jhalo_edge[0]->x(0) << ", "
12349 << jhalo_edge[0]->x(1) << ") and (" << jhalo_edge[1]->x(0)
12350 << ", " << jhalo_edge[1]->x(1) << ")\n\n"
12351 << "The nodes coordinates should be the same!!!\n";
12352 throw OomphLibError(err.str(),
12353 "TriangleMesh::create_polylines_from_"
12354 "halo_elements_helper()",
12355 OOMPH_EXCEPTION_LOCATION);
12356 }
12357
12358 // Store the edge
12359 edges[iproc][jproc].push_back(new_edge);
12360
12361 // Is the edge overlapped by a shared boundary
12362 if (edge_boundary_id >= 0)
12363 {
12364 // Mark the edge as overlapped
12365 overlapped_edge[new_edge] = true;
12366
12367 // Also mark the reversed edge
12368 std::pair<Node*, Node*> rev_new_edge =
12369 std::make_pair(ihalo_edge[1], ihalo_edge[0]);
12370
12371 // Mark the edge as overlapped
12372 overlapped_edge[rev_new_edge] = true;
12373 } // if (edge_boundary_id >= 0)
12374
12375 // Store the internal boundary id (default -1)
12376 // associated to the edge
12377 edge_boundary[iproc][jproc].push_back(edge_boundary_id);
12378
12379 // Store the two elements associated with the edge
12380 Vector<FiniteElement*> tmp_elements_pt;
12381 tmp_elements_pt.push_back(haloi_ele_pt);
12382 tmp_elements_pt.push_back(haloj_ele_pt);
12383
12384 // Associate the edge with the elements that gave rise to it
12385 edge_element_pt[iproc][jproc].push_back(tmp_elements_pt);
12386
12387 // Get the face index on each element that gave rise to
12388 // the edge
12389
12390 // .. first create the pair (edge, finite_element)
12391 std::pair<std::pair<Node*, Node*>, FiniteElement*>
12392 edge_elementi_pair = make_pair(new_edge, haloi_ele_pt);
12393
12394 std::pair<std::pair<Node*, Node*>, FiniteElement*>
12395 edge_elementj_pair =
12396 make_pair(new_edge_reversed, haloj_ele_pt);
12397
12398 // Set default values to later check if values were
12399 // read from the map structure
12400 int face_index_haloi_ele = -1;
12401 face_index_haloi_ele =
12402 edgesi_element_pt_to_face_index[edge_elementi_pair];
12403 int face_index_haloj_ele = -1;
12404 face_index_haloj_ele =
12405 edgesj_element_pt_to_face_index[edge_elementj_pair];
12406 // Verify that there is an element associated with it
12407 if (face_index_haloi_ele == -1 || face_index_haloj_ele == -1)
12408 {
12409 std::stringstream err;
12410 err << "There is no associated face indexes to the"
12411 << "elements that gave\nrise to the shared edge\n"
12412 << "The nodes that compound the edge are these:\n"
12413 << "On processor (" << iproc << "):\n"
12414 << "(" << ihalo_edge[0]->x(0) << ", "
12415 << ihalo_edge[0]->x(1) << ") and (" << ihalo_edge[1]->x(0)
12416 << ", " << ihalo_edge[1]->x(1) << ")\n\n"
12417 << "On processor (" << jproc << "):\n"
12418 << "(" << jhalo_edge[0]->x(0) << ", "
12419 << jhalo_edge[0]->x(1) << ") and (" << jhalo_edge[1]->x(0)
12420 << ", " << jhalo_edge[1]->x(1) << ")\n\n"
12421 << "The nodes coordinates should be the same!!!\n";
12422 throw OomphLibError(err.str(),
12423 "TriangleMesh::create_polylines_from_"
12424 "halo_elements_helper()",
12425 OOMPH_EXCEPTION_LOCATION);
12426 } // if (face_index_haloi_ele == -1 ||
12427 // face_index_haloj_ele == -1)
12428
12429 // Get the face indexes from the map structure
12430 Vector<int> tmp_edge_element_face_index;
12431 tmp_edge_element_face_index.push_back(face_index_haloi_ele);
12432 tmp_edge_element_face_index.push_back(face_index_haloj_ele);
12433 // Store the face indexes
12434 edge_element_face[iproc][jproc].push_back(
12435 tmp_edge_element_face_index);
12436
12437 break; // break for (jhe < nhalo_jedges) since edge found
12438
12439 } // else if (ihalo_edge[0] == jhalo_edge[1] &&
12440 // ihalo_edge[1] == jhalo_edge[0])
12441
12442 } // for (jhe < nhaloj_edges)
12443
12444 } // for (ihe < nhaloi_edges)
12445
12446 } // if (nhalo_elements_iproc_with_jproc > 0)
12447
12448 } // for (jproc < nproc)
12449
12450 } // for (iproc < nproc)
12451
12452 // ------------------------------------------------------------------
12453 // Compute the degree of each node in the shared edges
12454 // ------------------------------------------------------------------
12455
12456 // Visit all the shared edges between each pair of processors,
12457 // visit the nodes of each edge and compute the degree of each node
12458
12459 // Store the degree (valency) of each node
12460 std::map<Node*, unsigned> global_shared_node_degree;
12461
12462#ifdef PARANOID
12463 // Map to check if an edge has been already visited
12464 std::map<std::pair<Node*, Node*>, bool> edge_done;
12465#endif // #ifdef PARANOID
12466 // Map to check if a node has been already visited
12467 std::map<Node*, bool> node_done;
12468
12469 // Loop over the processors and get the shared edged between each
12470 // pair of processors
12471 for (unsigned iproc = 0; iproc < nproc; iproc++)
12472 {
12473 // Start from iproc + 1 to avoid checking with itself (there is
12474 // no shared edges between the same processor), and to avoid
12475 // double counting the edges and nodes (the shared edges between
12476 // processor (iproc, jproc) are the same as those between
12477 // processor jproc, iproc)
12478 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
12479 {
12480 // Get the number of edges shared between the pair of processors
12481 const unsigned nshd_edges = edges[iproc][jproc].size();
12482#ifdef PARANOID
12483 // There must be the same number of information on each of the
12484 // containers
12485
12486 // Get the number of edge elements
12487 const unsigned nedge_element = edge_element_pt[iproc][jproc].size();
12488 if (nshd_edges != nedge_element)
12489 {
12490 std::stringstream error_message;
12491 error_message
12492 << "The number of shared edges between processor iproc and jproc\n"
12493 << "is different form the number of edge elements between the\n"
12494 << "pair of processors\n"
12495 << "iproc: (" << iproc << ")\n"
12496 << "jproc: (" << jproc << ")\n"
12497 << "# of shared edges: (" << nshd_edges << ")\n"
12498 << "# of edge elements: (" << nedge_element << ")\n\n";
12499 throw OomphLibError(
12500 error_message.str(),
12501 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12502 OOMPH_EXCEPTION_LOCATION);
12503 }
12504
12505 // Get the number of edge element faces
12506 const unsigned nedge_element_face =
12507 edge_element_face[iproc][jproc].size();
12508 if (nshd_edges != nedge_element_face)
12509 {
12510 std::stringstream error_message;
12511 error_message
12512 << "The number of shared edges between processor iproc and jproc\n"
12513 << "is different form the number of edge element faces between "
12514 "the\n"
12515 << "pair of processors\n"
12516 << "iproc: (" << iproc << ")\n"
12517 << "jproc: (" << jproc << ")\n"
12518 << "# of shared edges: (" << nshd_edges << ")\n"
12519 << "# of edge element faces: (" << nedge_element_face << ")\n\n";
12520 throw OomphLibError(
12521 error_message.str(),
12522 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12523 OOMPH_EXCEPTION_LOCATION);
12524 }
12525
12526 // Get the number of edge boundaries
12527 const unsigned nedge_boundary = edge_boundary[iproc][jproc].size();
12528 if (nshd_edges != nedge_boundary)
12529 {
12530 std::stringstream error_message;
12531 error_message
12532 << "The number of shared edges between processor iproc and jproc\n"
12533 << "is different form the number of edge boundaries ids between "
12534 "the\n"
12535 << "pair of processors\n"
12536 << "iproc: (" << iproc << ")\n"
12537 << "jproc: (" << jproc << ")\n"
12538 << "# of shared edges: (" << nshd_edges << ")\n"
12539 << "# of edge boundaries ids: (" << nedge_boundary << ")\n\n";
12540 throw OomphLibError(
12541 error_message.str(),
12542 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12543 OOMPH_EXCEPTION_LOCATION);
12544 }
12545
12546#endif // #ifdef PARANOID
12547
12548 // Loop over the shared edges between (iproc, jproc) processors
12549 for (unsigned se = 0; se < nshd_edges; se++)
12550 {
12551 // Get the edge
12552 std::pair<Node*, Node*> edge = edges[iproc][jproc][se];
12553#ifdef PARANOID
12554 // Check that the edge has not been previously visited
12555 if (edge_done[edge])
12556 {
12557 std::stringstream error_message;
12558 error_message
12559 << "The shared edge between processor iproc and processor\n"
12560 << "jproc has been already visited, this is weird since the\n"
12561 << "edge should not be shared by other pair of processors\n"
12562 << "iproc: (" << iproc << ")\n"
12563 << "jproc: (" << jproc << ")\n"
12564 << "First node of edge: (" << edge.first->x(0) << ", "
12565 << edge.first->x(1) << ")\n"
12566 << "Second node of edge: (" << edge.second->x(0) << ", "
12567 << edge.second->x(1) << ")\n"
12568 << "Associated edge boundary id: ("
12569 << edge_boundary[iproc][jproc][se] << ")\n\n";
12570 throw OomphLibError(
12571 error_message.str(),
12572 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12573 OOMPH_EXCEPTION_LOCATION);
12574 }
12575
12576 // Mark the edge as done
12577 edge_done[edge] = true;
12578 // Create the reversed version and include it too
12579 std::pair<Node*, Node*> rev_edge =
12580 std::make_pair(edge.second, edge.first);
12581 // Mark reversed edge as done
12582 edge_done[rev_edge] = true;
12583#endif // #ifdef PARANOID
12584
12585 // Get each of the nodes that conform the edge
12586 Node* left_node_pt = edge.first;
12587 Node* right_node_pt = edge.second;
12588
12589 // Check if the left node has been already done
12590 if (!node_done[left_node_pt])
12591 {
12592 // Set the degree of the node to once since this is the
12593 // first time it has been found
12594 global_shared_node_degree[left_node_pt] = 1;
12595
12596 } // if (!done_node[left_node_pt])
12597 else
12598 {
12599 // Increase the degree of the node
12600 global_shared_node_degree[left_node_pt]++;
12601 }
12602
12603 // Check if the right node has been already done
12604 if (!node_done[right_node_pt])
12605 {
12606 // Set the degree of the node to once since this is the
12607 // first time it has been found
12608 global_shared_node_degree[right_node_pt] = 1;
12609 } // if (!done_node[right_node_pt])
12610 else
12611 {
12612 // Increase the degree of the node
12613 global_shared_node_degree[right_node_pt]++;
12614 }
12615
12616 } // for (se < nshd_edges)
12617
12618 } // for (jproc < nproc)
12619
12620 } // for (iproc < nproc)
12621
12622 // -----------------------------------------------------------------
12623 // Identify those nodes living on edges of original boundaries not
12624 // overlapped by a shared boundary
12625
12626 // Mark the nodes on original boundaries not overlapped by shared
12627 // boundaries
12628 std::map<unsigned, std::map<Node*, bool>>
12629 node_on_bnd_not_overlapped_by_shd_bnd;
12630
12631 // Loop over the edges of the original boundaries
12632 for (std::map<std::pair<Node*, Node*>, unsigned>::iterator it_map =
12633 elements_edges_on_boundary.begin();
12634 it_map != elements_edges_on_boundary.end();
12635 it_map++)
12636 {
12637 // Get the edge
12638 std::pair<Node*, Node*> edge_pair = (*it_map).first;
12639
12640 // Is the edge overlaped by a shared boundary
12641 if (!overlapped_edge[edge_pair])
12642 {
12643 // Mark the nodes of the edge as being on an edge not overlaped
12644 // by a shared boundary on the boundary the edge is
12645 unsigned b = (*it_map).second;
12646
12647 // Get the left node
12648 Node* left_node_pt = edge_pair.first;
12649 node_on_bnd_not_overlapped_by_shd_bnd[b][left_node_pt] = true;
12650
12651 // Get the right node
12652 Node* right_node_pt = edge_pair.second;
12653 node_on_bnd_not_overlapped_by_shd_bnd[b][right_node_pt] = true;
12654
12655 } // if (!overlapped_edge[edge_pair])
12656
12657 } // Loop over edges to mark those nodes on overlaped edge by
12658 // shared boundaries
12659
12660 // ------------------------------------------------------------------
12661 // Now create the shared polylines but including the degree of the
12662 // nodes as a nw stop condition for adding more edges to the side
12663 // or a root edge
12664 // ------------------------------------------------------------------
12665
12666 // Storage for new created polylines with "each processor", non
12667 // sorted (shared polylines of the current processor only)
12668 Vector<Vector<TriangleMeshPolyLine*>> unsorted_polylines_pt(nproc);
12669
12670 // Map that associates the shared boundary id with the list of
12671 // nodes that create it (shared boundary of the current processor
12672 // only)
12673 std::map<unsigned, std::list<Node*>> shared_bnd_id_to_sorted_list_node_pt;
12674
12675 // Get maximum user boundary id and set the initial shared boundary
12676 // id
12677 unsigned shared_boundary_id_start = this->nboundary();
12678 Initial_shared_boundary_id = shared_boundary_id_start;
12679
12680 // Aqui
12681
12682 // Loop over the processors and get the shared edged between each
12683 // pair of processors
12684 for (unsigned iproc = 0; iproc < nproc; iproc++)
12685 {
12686 // Start from iproc + 1 to avoid checking with itself (there is
12687 // no shared edges between the same processor), and to avoid
12688 // double counting the edges and nodes (the shared edges between
12689 // processor (iproc, jproc) are the same as those between
12690 // processor jproc, iproc)
12691 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
12692 {
12693 // *************************************************************
12694 // THIRD PART
12695 // 1) Sort the edges (make them contiguous) so that they can
12696 // be used as the vertex coordinates that define a shared
12697 // boundary (polyline)
12698 // *************************************************************
12699 unsigned npolylines_counter = 0;
12700 const unsigned nedges = edges[iproc][jproc].size();
12701
12702 // -----------------------------------------------------------
12703 // Compute all the SHARED POLYLINES
12704 // -----------------------------------------------------------
12705 // The number of sorted edges
12706 unsigned nsorted_edges = 0;
12707
12708 // Keep track of the already done edges
12709 std::map<std::pair<Node*, Node*>, bool> edge_done;
12710
12711 // Loop over all the edges to create all the polylines with
12712 // the current processors involved
12713 while (nsorted_edges < nedges)
12714 {
12715 // Temporaly storage for the elements associated to the
12716 // sorted edges
12717 std::list<FiniteElement*> tmp_boundary_element_pt;
12718 // Temporly storage for the face indexes on the element
12719 // that created the given edge
12720 std::list<int> tmp_face_index_element;
12721 // Get an initial pair of nodes to create an edge
12722 std::pair<Node*, Node*> edge;
12723#ifdef PARANOID
12724 bool found_initial_edge = false;
12725#endif
12726 int root_edge_bound_id = -1;
12727 unsigned iedge = 0;
12728 for (iedge = 0; iedge < nedges; iedge++)
12729 {
12730 edge = edges[iproc][jproc][iedge];
12731 // If not done then take it as initial edge
12732 if (!edge_done[edge])
12733 {
12734 // Get the boundary id that the edge may be overlapping
12735 root_edge_bound_id = edge_boundary[iproc][jproc][iedge];
12736#ifdef PARANOID
12737 found_initial_edge = true;
12738#endif
12739 nsorted_edges++;
12740 iedge++;
12741 break;
12742 } // if (!edge_done[edge])
12743 } // for (iedge < nedges)
12744
12745#ifdef PARANOID
12746 if (!found_initial_edge)
12747 {
12748 std::ostringstream error_message;
12749 error_message
12750 << "All the edge are already done, but the number of done\n"
12751 << "edges (" << nsorted_edges
12752 << ") is still less than the total\n"
12753 << "number of edges (" << nedges << ").\n";
12754 // << "----- Possible memory leak -----\n";
12755 throw OomphLibError(
12756 error_message.str(),
12757 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12758 OOMPH_EXCEPTION_LOCATION);
12759 }
12760#endif
12761
12762 // Storing for the sorting nodes extracted from the
12763 // edges. The sorted nodes are used to create a polyline
12764 std::list<Node*> sorted_nodes;
12765 sorted_nodes.clear();
12766
12767 // The initial and final nodes of the list
12768 Node* first_node_pt = edge.first;
12769 Node* last_node_pt = edge.second;
12770
12771 // Push back on the list the new edge (nodes)
12772 sorted_nodes.push_back(first_node_pt);
12773 sorted_nodes.push_back(last_node_pt);
12774
12775 // Get the elements associated to the edge and store them
12776 // in the temporaly boundary elements storage
12777 tmp_boundary_element_pt.push_back(
12778 edge_element_pt[iproc][jproc][iedge - 1][0]);
12779 tmp_boundary_element_pt.push_back(
12780 edge_element_pt[iproc][jproc][iedge - 1][1]);
12781
12782 // ... then get the face index of the element from where
12783 // the edge came from
12784 tmp_face_index_element.push_back(
12785 edge_element_face[iproc][jproc][iedge - 1][0]);
12786 tmp_face_index_element.push_back(
12787 edge_element_face[iproc][jproc][iedge - 1][1]);
12788
12789 // Mark edge as done
12790 edge_done[edge] = true;
12791
12792 // Continue iterating if a new node (that creates a new
12793 // edge) is added to the list, we have just added two nodes
12794 // (the first and last of the root edge)
12795 bool node_added = true;
12796
12797 // Flags to indicate at which end the node was added (left
12798 // or right)
12799 bool node_added_to_the_left = true;
12800 bool node_added_to_the_right = true;
12801
12802 // The nodes that create a shared boundary are obtained by
12803 // connecting the edges shared by the halo and haloed
12804 // elements. These edges are connected to left or right of
12805 // the shared boundary. Every time a new edge is added to
12806 // the left (or right), the most left (or right) node is
12807 // searched in the list of nodes of previous shared
12808 // boundaries, if the node is found then it is said to be
12809 // shared with another boundary and a connection to that
12810 // boundary needs to be specified. We stop adding edges
12811 // (and nodes) to the side where that nodes was found to be
12812 // shared. Note that the intersection (shared node) may be
12813 // with the same shared boundary
12814
12815 // Flag to indicate a node was found to be shared with
12816 // another boundary at the left end (most left node) of the
12817 // shared boundary
12818 bool connection_to_the_left = false;
12819
12820 // Flag to indicate a node was found to be shared with
12821 // another boundary at the right end (most right node) of
12822 // the shared boundary
12823 bool connection_to_the_right = false;
12824
12825 // Flag to stop the adding of edges (and nodes) to the
12826 // current shared boundary
12827 bool current_polyline_has_connections_at_both_ends = false;
12828
12829 // Store the boundary ids of the polylines to connect (only
12830 // used when the polyline was found to have a connection)
12831 // -1: Indicates no connection
12832 // -2: Indicates connection with itself
12833 // Any other value: Boundary id to connect
12834 int bound_id_connection_to_the_left = -1;
12835 int bound_id_connection_to_the_right = -1;
12836
12837 // Get the degree of the first node
12838 const unsigned first_node_degree =
12839 global_shared_node_degree[first_node_pt];
12840
12841 // Check if the nodes of the root edge have connections
12842 // ... to the left
12843 bound_id_connection_to_the_left = check_connections_of_polyline_nodes(
12844 element_in_processor_pt,
12845 root_edge_bound_id,
12846 overlapped_edge,
12847 node_on_bnd_not_overlapped_by_shd_bnd,
12848 sorted_nodes,
12849 shared_bnd_id_to_sorted_list_node_pt,
12850 first_node_degree,
12851 first_node_pt);
12852
12853 // If there is a connection then set the
12854 // corresponding flag
12855 // (-1): No connection
12856 // (-2): Connection with itself
12857 // (-3): No connection, stop adding nodes
12858 // (other value): Boundary id
12859 if (bound_id_connection_to_the_left != -1)
12860 {
12861 connection_to_the_left = true;
12862 } // if (bound_id_connection_to_the_left != -1)
12863
12864 // Get the degree of the last node
12865 const unsigned last_node_degree =
12866 global_shared_node_degree[last_node_pt];
12867
12868 // Check if the nodes of the root edge have connections
12869 // ... to the right
12870 bound_id_connection_to_the_right =
12871 check_connections_of_polyline_nodes(
12872 element_in_processor_pt,
12873 root_edge_bound_id,
12874 overlapped_edge,
12875 node_on_bnd_not_overlapped_by_shd_bnd,
12876 sorted_nodes,
12877 shared_bnd_id_to_sorted_list_node_pt,
12878 last_node_degree,
12879 last_node_pt);
12880
12881 // If there is a connection then set the
12882 // corresponding flag
12883 // (-1): No connection
12884 // (-2): Connection with itself
12885 // (other value): Boundary id
12886 if (bound_id_connection_to_the_right != -1)
12887 {
12888 connection_to_the_right = true;
12889 } // if (bound_id_connection_to_the_right != -1)
12890
12891 // If the current shared boundary has connections at both
12892 // ends then stop the adding of nodes
12893 if (connection_to_the_left && connection_to_the_right)
12894 {
12895 current_polyline_has_connections_at_both_ends = true;
12896 }
12897
12898 // Continue searching for more edges if
12899 // 1) A new node was added at the left or right of the list
12900 // 2) There are more edges to possible add
12901 // 3) The added node is not part of any other previous
12902 // shared polyline
12903 while (node_added && (nsorted_edges < nedges) &&
12904 !current_polyline_has_connections_at_both_ends)
12905 {
12906 // Start from the next edge since we have already added
12907 // the previous one as the initial edge (any previous
12908 // edge had to be added to previous polylines)
12909 for (unsigned iiedge = iedge; iiedge < nedges; iiedge++)
12910 {
12911 // Reset the flags for added nodes, to the left and right
12912 node_added = false;
12913 node_added_to_the_left = false;
12914 node_added_to_the_right = false;
12915 // Get the current edge
12916 edge = edges[iproc][jproc][iiedge];
12917 const int edge_bound_id = edge_boundary[iproc][jproc][iiedge];
12918
12919 // We need to ensure to connect with edges that share
12920 // the same bound id or with those that has no boundary
12921 // id associated (the default -1 value), may apply
12922 // exclusively to internal boundaries
12923 if (!edge_done[edge] && (edge_bound_id == root_edge_bound_id))
12924 {
12925 // Get each individual node
12926 Node* left_node_pt = edge.first;
12927 Node* right_node_pt = edge.second;
12928
12929 // Pointer to the new added node
12930 Node* new_added_node_pt = 0;
12931
12932 // Is the node to be added to the left?
12933 if (left_node_pt == first_node_pt && !connection_to_the_left)
12934 {
12935 // Push front the new node
12936 sorted_nodes.push_front(right_node_pt);
12937 // Update the new added node and the first node
12938 new_added_node_pt = first_node_pt = right_node_pt;
12939 // Set the node added flag to true
12940 node_added = true;
12941 // Indicate the node was added to the left
12942 node_added_to_the_left = true;
12943 }
12944 // Is the node to be added to the right?
12945 else if (left_node_pt == last_node_pt &&
12946 !connection_to_the_right)
12947 {
12948 // Push back the new node
12949 sorted_nodes.push_back(right_node_pt);
12950 // Update the new added node and the last node
12951 new_added_node_pt = last_node_pt = right_node_pt;
12952 // Set the node added flag to true
12953 node_added = true;
12954 // Indicate the node was added to the right
12955 node_added_to_the_right = true;
12956 }
12957 // Is the node to be added to the left?
12958 else if (right_node_pt == first_node_pt &&
12959 !connection_to_the_left)
12960 {
12961 // Push front the new node
12962 sorted_nodes.push_front(left_node_pt);
12963 // Update the new added node and the first node
12964 new_added_node_pt = first_node_pt = left_node_pt;
12965 // Set the node added flag to true
12966 node_added = true;
12967 // Indicate the node was added to the left
12968 node_added_to_the_left = true;
12969 }
12970 // Is the node to be added to the right?
12971 else if (right_node_pt == last_node_pt &&
12972 !connection_to_the_right)
12973 {
12974 // Push back the new node
12975 sorted_nodes.push_back(left_node_pt);
12976 // Update the new added node and the last node
12977 new_added_node_pt = last_node_pt = left_node_pt;
12978 // Set the node added flag to true
12979 node_added = true;
12980 // Indicate the node was added to the right
12981 node_added_to_the_right = true;
12982 }
12983
12984 // If we added a new node then we need to check if
12985 // that node has been already added in other shared
12986 // boundaries (which may define a connection)
12987 if (node_added)
12988 {
12989 // Mark as done only if one of its nodes has been
12990 // added to the list
12991 edge_done[edge] = true;
12992 nsorted_edges++;
12993
12994 // Get the degree of the added node
12995 const unsigned added_node_degree =
12996 global_shared_node_degree[new_added_node_pt];
12997
12998 if (node_added_to_the_left)
12999 {
13000 // Add the bulk elements
13001 tmp_boundary_element_pt.push_front(
13002 edge_element_pt[iproc][jproc][iiedge][1]);
13003 tmp_boundary_element_pt.push_front(
13004 edge_element_pt[iproc][jproc][iiedge][0]);
13005 // Add the face elements
13006 tmp_face_index_element.push_front(
13007 edge_element_face[iproc][jproc][iiedge][1]);
13008 tmp_face_index_element.push_front(
13009 edge_element_face[iproc][jproc][iiedge][0]);
13010 }
13011
13012 if (node_added_to_the_right)
13013 {
13014 // Add the bulk elements
13015 tmp_boundary_element_pt.push_back(
13016 edge_element_pt[iproc][jproc][iiedge][0]);
13017 tmp_boundary_element_pt.push_back(
13018 edge_element_pt[iproc][jproc][iiedge][1]);
13019 // Add the face elements
13020 tmp_face_index_element.push_back(
13021 edge_element_face[iproc][jproc][iiedge][0]);
13022 tmp_face_index_element.push_back(
13023 edge_element_face[iproc][jproc][iiedge][1]);
13024 }
13025
13026 // Based on which side the node was added, look for
13027 // connections on that side
13028
13029 // Verify for connections to the left (we need to
13030 // check for the connection variable too, since
13031 // after a connection has been done we no longer
13032 // need to verify for this condition)
13033 if (node_added_to_the_left && !connection_to_the_left)
13034 {
13035 // Check for connection
13036 bound_id_connection_to_the_left =
13037 check_connections_of_polyline_nodes(
13038 element_in_processor_pt,
13039 root_edge_bound_id,
13040 overlapped_edge,
13041 node_on_bnd_not_overlapped_by_shd_bnd,
13042 sorted_nodes,
13043 shared_bnd_id_to_sorted_list_node_pt,
13044 added_node_degree,
13045 new_added_node_pt);
13046
13047 // If there is a connection then set the
13048 // corresponding flag
13049 // (-1): No connection
13050 // (-2): Connection with itself
13051 // (other value): Boundary id
13052 if (bound_id_connection_to_the_left != -1)
13053 {
13054 connection_to_the_left = true;
13055 } // if (bound_id_connection_to_the_left != -1)
13056
13057 } // if (node_added_to_the_left &&
13058 // !connection_to_the_left)
13059
13060 // Verify for connections to the right (we need to
13061 // check for the connection variable too, since
13062 // after a connection has been done we no longer
13063 // need to verify for this condition)
13064 if (node_added_to_the_right && !connection_to_the_right)
13065 {
13066 // Check for connection
13067 bound_id_connection_to_the_right =
13068 check_connections_of_polyline_nodes(
13069 element_in_processor_pt,
13070 root_edge_bound_id,
13071 overlapped_edge,
13072 node_on_bnd_not_overlapped_by_shd_bnd,
13073 sorted_nodes,
13074 shared_bnd_id_to_sorted_list_node_pt,
13075 added_node_degree,
13076 new_added_node_pt);
13077
13078 // If there is a connection then set the
13079 // corresponding flag
13080 // (-1): No connection
13081 // (-2): Connection with itself
13082 // (other value): Boundary id
13083 if (bound_id_connection_to_the_right != -1)
13084 {
13085 connection_to_the_right = true;
13086 } // if (bound_id_connection_to_the_right != -1)
13087
13088 } // if (node_added_to_the_right &&
13089 // !connection_to_the_right)
13090
13091 // If the current shared boundary has connections
13092 // at both ends then stop the adding of nodes
13093 if (connection_to_the_left && connection_to_the_right)
13094 {
13095 current_polyline_has_connections_at_both_ends = true;
13096 }
13097
13098 // Break the for and re-start to look more edges to
13099 // the left or right
13100 break;
13101
13102 } // if (node_added)
13103
13104 } // if (!edge_done[edge])
13105 } // for (iiedge < nedges)
13106
13107 } // while(node_added && (nsorted_edges < nedges)
13108 // && !current_polyline_has_connections_at_both_ends)
13109
13110 // ------------------------------------------------------------
13111 // If the sorted nodes of the shared polyline create a loop
13112 // it is necessary to break it by creating as many
13113 // polylines as required
13114
13115 // Change the list to a vector representation of the
13116 // boundary elements and the face indexes
13117
13118 // Get the number of boundary elements
13119 const unsigned n_bnd_ele = tmp_boundary_element_pt.size();
13120
13121 // Storage for the boundary elements and face indexes
13122 Vector<FiniteElement*> tmp_bnd_ele_pt(n_bnd_ele);
13123 Vector<int> tmp_face_idx_ele(n_bnd_ele);
13124 // Helper counter
13125 unsigned help_counter = 0;
13126 // Fill the data structures
13127 for (std::list<FiniteElement*>::iterator it_bnd_ele =
13128 tmp_boundary_element_pt.begin();
13129 it_bnd_ele != tmp_boundary_element_pt.end();
13130 it_bnd_ele++)
13131 {
13132 tmp_bnd_ele_pt[help_counter++] = (*it_bnd_ele);
13133 }
13134
13135 // Restart counter
13136 help_counter = 0;
13137 for (std::list<int>::iterator it_face_idx =
13138 tmp_face_index_element.begin();
13139 it_face_idx != tmp_face_index_element.end();
13140 it_face_idx++)
13141 {
13142 tmp_face_idx_ele[help_counter++] = (*it_face_idx);
13143 }
13144
13145 // Store the nodes for the new shared polylines without
13146 // loops
13147 Vector<std::list<Node*>> final_sorted_nodes_pt;
13148 // Store the boundary elements of the shared polyline
13149 // without loops
13150 Vector<Vector<FiniteElement*>> final_boundary_element_pt;
13151 // Face indexes of the boundary elements without loops
13152 Vector<Vector<int>> final_face_index_element;
13153 // Connection flags (to the left) of the shared boundaries
13154 // without loops
13155 Vector<int> final_bound_id_connection_to_the_left;
13156 // Connection flags (to the right) of the shared boundaries
13157 // without loops
13158 Vector<int> final_bound_id_connection_to_the_right;
13159
13160 // Break any possible loop created by the shared polyline
13161 break_loops_on_shared_polyline_helper(
13162 shared_boundary_id_start,
13163 sorted_nodes,
13164 tmp_bnd_ele_pt,
13165 tmp_face_idx_ele,
13166 bound_id_connection_to_the_left,
13167 bound_id_connection_to_the_right,
13168 final_sorted_nodes_pt,
13169 final_boundary_element_pt,
13170 final_face_index_element,
13171 final_bound_id_connection_to_the_left,
13172 final_bound_id_connection_to_the_right);
13173
13174 // Get the number of final sorted nodes
13175 const unsigned n_final_sorted_nodes = final_sorted_nodes_pt.size();
13176
13177 // Loop over the list of final sorted nodes
13178 for (unsigned i = 0; i < n_final_sorted_nodes; i++)
13179 {
13180 // --------------------------------------------------------
13181 // Associate the list of sorted nodes with the boundary id
13182 // of the shared boundary that is going to be crated
13183 shared_bnd_id_to_sorted_list_node_pt[shared_boundary_id_start] =
13184 final_sorted_nodes_pt[i];
13185
13186 // Create the shared polyline and fill the data
13187 // structured associated to it
13188 create_shared_polyline(my_rank,
13189 shared_boundary_id_start,
13190 iproc,
13191 jproc,
13192 final_sorted_nodes_pt[i],
13193 root_edge_bound_id,
13194 final_boundary_element_pt[i],
13195 final_face_index_element[i],
13196 unsorted_polylines_pt,
13197 final_bound_id_connection_to_the_left[i],
13198 final_bound_id_connection_to_the_right[i]);
13199
13200 // Increase the register for the number of created shared
13201 // polylines
13202 npolylines_counter++;
13203
13204 // Increase the boundary id (the one that will be used by
13205 // the next shared boundary)
13206 shared_boundary_id_start++;
13207
13208 } // for (i < n_final_sorted_nodes)
13209
13210 } // while(nsorted_edges < nedges);
13211
13212 } // for (jproc < nproc)
13213
13214 // We already have all the shared polylines (shared boundaries)
13215 // of processor iproc with processor jproc. Now we sort them so
13216 // that they be contiguous and can create polygons.
13217
13218 // If there are polylines to be sorted then sort them
13219 if (unsorted_polylines_pt[iproc].size() > 0)
13220 {
13221 // Now that we have all the new unsorted polylines on "iproc"
13222 // processor it is time to sort them so they be all contiguous
13223 sort_polylines_helper(unsorted_polylines_pt[iproc],
13224 output_polylines_pt[iproc]);
13225 }
13226
13227#ifdef PARANOID
13228 const unsigned nunsorted_polylines_iproc =
13229 unsorted_polylines_pt[iproc].size();
13230
13231 // Verify that all the polylines have been sorted
13232 unsigned tmp_ntotal_polylines = 0;
13233 // Count the total number of sorted polylines
13234 for (unsigned ii = 0; ii < output_polylines_pt[iproc].size(); ii++)
13235 {
13236 tmp_ntotal_polylines += output_polylines_pt[iproc][ii].size();
13237 }
13238 if (tmp_ntotal_polylines != nunsorted_polylines_iproc)
13239 {
13240 std::ostringstream error_message;
13241 error_message << " The total number of unsorted polylines ("
13242 << nunsorted_polylines_iproc
13243 << ") in common with\nprocessor (" << iproc
13244 << ") is different from the total number of sorted "
13245 << "polylines (" << tmp_ntotal_polylines
13246 << ") with\nthe same "
13247 << "proessor\n";
13248 throw OomphLibError(error_message.str(),
13249 OOMPH_CURRENT_FUNCTION,
13250 OOMPH_EXCEPTION_LOCATION);
13251 } // if (tmp_ntotal_polylines != nunsorted_polylines_iproc)
13252#endif
13253
13254 } // for (iproc < nproc)
13255
13256 // Establish the last used boundary id
13257 this->Final_shared_boundary_id = shared_boundary_id_start;
13258 }
13259
13260 // ======================================================================
13261 // Break any possible loop created by the sorted list of nodes
13262 // that is used to create a new shared polyline
13263 // ======================================================================
13264 template<class ELEMENT>
13266 const unsigned& initial_shd_bnd_id,
13267 std::list<Node*>& input_nodes,
13268 Vector<FiniteElement*>& input_boundary_element_pt,
13269 Vector<int>& input_face_index_element,
13270 const int& input_connect_to_the_left,
13271 const int& input_connect_to_the_right,
13272 Vector<std::list<Node*>>& output_sorted_nodes_pt,
13273 Vector<Vector<FiniteElement*>>& output_boundary_element_pt,
13274 Vector<Vector<int>>& output_face_index_element,
13275 Vector<int>& output_connect_to_the_left,
13276 Vector<int>& output_connect_to_the_right)
13277 {
13278 // Get the left and right node of the current list of sorted nodes
13279 Node* left_node_pt = input_nodes.front();
13280 Node* right_node_pt = input_nodes.back();
13281
13282 // Temporary storage for list of nodes, boundary elements and face
13283 // element's indexes
13284 Vector<std::list<Node*>> tmp_sub_nodes;
13285 Vector<Vector<FiniteElement*>> tmp_sub_bnd_ele_pt;
13286 Vector<Vector<int>> tmp_sub_face_idx_ele;
13287
13288 // Iterator for the list of input nodes
13289 std::list<Node*>::iterator it = input_nodes.begin();
13290
13291 // Counter
13292 unsigned counter = 0;
13293
13294 // Loop while not all nodes have been done
13295 while (it != input_nodes.end())
13296 {
13297 // Check if the current node is the final one
13298 it++;
13299 // Is the current node the final node?
13300 if (it == input_nodes.end())
13301 {
13302 // Break, add no more nodes
13303 break;
13304 }
13305 else
13306 {
13307 // Restore the iterator
13308 it--;
13309 }
13310
13311 // Get a list of nonrepeated nodes
13312 std::list<Node*> sub_nodes;
13313 // The temporary vector of boundary elements associated with the
13314 // nodes
13315 Vector<FiniteElement*> sub_bnd_ele_pt;
13316 // The temporary vector of face indexes associated with the
13317 // boundary elements
13318 Vector<int> sub_face_idx_ele;
13319
13320 // Add the current node to the list
13321 sub_nodes.push_back(*it);
13322
13323 // Add nodes until found a repeated node (the left or right
13324 // node) or until reaching the end of the list of nodes
13325 do
13326 {
13327 // Go to the next node
13328 ++it;
13329
13330 // Add the new node
13331 sub_nodes.push_back((*it));
13332
13333 // Add the boundary elements
13334 sub_bnd_ele_pt.push_back(input_boundary_element_pt[counter]);
13335 sub_bnd_ele_pt.push_back(input_boundary_element_pt[counter + 1]);
13336
13337 // Add the face indexes
13338 sub_face_idx_ele.push_back(input_face_index_element[counter]);
13339 sub_face_idx_ele.push_back(input_face_index_element[counter + 1]);
13340
13341 // Increase the counter
13342 counter += 2;
13343
13344 // Continue adding until reaching a repeated node or the end
13345 // of the list of nodes
13346 } while ((*it) != left_node_pt && (*it) != right_node_pt &&
13347 it != input_nodes.end());
13348
13349 // Add the sub-set of nodes to the temporary storage
13350 tmp_sub_nodes.push_back(sub_nodes);
13351 // Add the face elements to the temporary storage
13352 tmp_sub_bnd_ele_pt.push_back(sub_bnd_ele_pt);
13353 // Add the face indexes to the temporary storage
13354 tmp_sub_face_idx_ele.push_back(sub_face_idx_ele);
13355
13356 } // while((*it) != input_nodes.end())
13357
13358 // --------------------------------------------------
13359 // Now create as many shared boundaries as required
13360
13361 // Get the number of sub-list of nodes created
13362 const unsigned n_sub_list = tmp_sub_nodes.size();
13363
13364#ifdef PARANOID
13365 if (n_sub_list > 3)
13366 {
13367 std::stringstream error_message;
13368 error_message
13369 << "The number of sub-list of nodes created from the shared\n"
13370 << "polyline with loops was (" << n_sub_list << ").\n"
13371 << "We can only handle up to three sub-list of nodes\n";
13372 throw OomphLibError(
13373 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
13374 }
13375#endif
13376
13377 // If there is only one list it may be because there are no loops or
13378 // there is only one loop (a circle)
13379 if (n_sub_list == 1 && (left_node_pt != right_node_pt))
13380 {
13381 // There are no loops, return just after filling the data
13382 // structures
13383
13384 // This is the base case used most of the times
13385
13386 // Set the vector of lists of nodes
13387 output_sorted_nodes_pt = tmp_sub_nodes;
13388 // Set the vector of boundary elements
13389 output_boundary_element_pt = tmp_sub_bnd_ele_pt;
13390 // Set the vector of face indexes
13391 output_face_index_element = tmp_sub_face_idx_ele;
13392
13393 // Set the connection flags, change them by the proper connection
13394 // flag
13395
13396#ifdef PARANOID
13397 if (input_connect_to_the_left == -2)
13398 {
13399 std::stringstream error_message;
13400 error_message
13401 << "The connection flag to the left (" << input_connect_to_the_left
13402 << ") indicates a connection\n"
13403 << "with the same polyline.\n However, only one sub-polyline was "
13404 << "found and no loop\nwas identified\n\n";
13405 throw OomphLibError(error_message.str(),
13406 OOMPH_CURRENT_FUNCTION,
13407 OOMPH_EXCEPTION_LOCATION);
13408 }
13409#endif
13410
13411 // The left connection flag
13412 if (input_connect_to_the_left == -3)
13413 {
13414 output_connect_to_the_left.push_back(-1);
13415 }
13416 else
13417 {
13418 output_connect_to_the_left.push_back(input_connect_to_the_left);
13419 }
13420
13421#ifdef PARANOID
13422 if (input_connect_to_the_right == -2)
13423 {
13424 std::stringstream error_message;
13425 error_message
13426 << "The connection flag to the right (" << input_connect_to_the_right
13427 << ") indicates a connection\n"
13428 << "with the same polyline.\n However, only one sub-polyline was "
13429 << "found and no loop\nwas identified\n\n";
13430 throw OomphLibError(
13431 error_message.str(),
13432 "TriangleMesh::break_loops_on_shared_polyline_helper()",
13433 OOMPH_EXCEPTION_LOCATION);
13434 }
13435#endif
13436
13437 // The right connection flag
13438 if (input_connect_to_the_right == -3)
13439 {
13440 output_connect_to_the_right.push_back(-1);
13441 }
13442 else
13443 {
13444 output_connect_to_the_right.push_back(input_connect_to_the_right);
13445 }
13446
13447 // Return inmediately
13448 return;
13449 }
13450
13451 // The temporary storage for the shared boundary id
13452 unsigned tmp_shd_bnd_id = initial_shd_bnd_id;
13453
13454 // -----------------------------------------------------------------
13455 // Check all the sub-list of nodes and create two shared boundaries
13456 // from those that make a loop (circle)
13457
13458 // -----------------------------------------------------------
13459 // Get the left and right node of the first sub-list of nodes
13460 Node* left_sub_node_pt = tmp_sub_nodes[0].front();
13461 Node* right_sub_node_pt = tmp_sub_nodes[0].back();
13462
13463 // Check if the sub-list of nodes creates a loop (circle)
13464 if (left_sub_node_pt == right_sub_node_pt)
13465 {
13466 // We need to create two shared polylines and therefore increase
13467 // the shared boundary id by two
13468
13469 // The first and second half of nodes
13470 std::list<Node*> first_half_node_pt;
13471 std::list<Node*> second_half_node_pt;
13472 // The first and second half of boundary elements
13473 Vector<FiniteElement*> first_half_ele_pt;
13474 Vector<FiniteElement*> second_half_ele_pt;
13475 // The first and second half of face indexes
13476 Vector<int> first_half_face_idx;
13477 Vector<int> second_half_face_idx;
13478
13479 // Get the number of sub-nodes in the sub-list of nodes
13480 const unsigned n_sub_nodes = tmp_sub_nodes[0].size();
13481
13482 // The number of sub-nodes for the first half of the shared
13483 // boundary
13484 const unsigned n_sub_nodes_half =
13485 static_cast<unsigned>(n_sub_nodes / 2.0);
13486
13487 // Copy as many sub-nodes for the first half of the sub-polyline
13488
13489 // Iterator to loop over the nodes
13490 std::list<Node*>::iterator it_sub = tmp_sub_nodes[0].begin();
13491
13492 // Add the first node
13493 first_half_node_pt.push_back(*it_sub);
13494
13495 // Skip the first node
13496 it_sub++;
13497
13498 // Counter
13499 unsigned counter_nodes = 0;
13500 unsigned counter2 = 0;
13501
13502 // Loop to copy the nodes
13503 for (; it_sub != tmp_sub_nodes[0].end(); it_sub++)
13504 {
13505 // Add the sub-node to the first half
13506 first_half_node_pt.push_back(*it_sub);
13507
13508 // Add the boundary elements of the first half
13509 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[0][counter2]);
13510 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[0][counter2 + 1]);
13511 // Add the face indexes of the first half
13512 first_half_face_idx.push_back(tmp_sub_face_idx_ele[0][counter2]);
13513 first_half_face_idx.push_back(tmp_sub_face_idx_ele[0][counter2 + 1]);
13514
13515 // Increase the counter of added nodes
13516 counter_nodes++;
13517
13518 // Increase the other counter
13519 counter2 += 2;
13520
13521 if (counter_nodes == n_sub_nodes_half)
13522 {
13523 // Stop adding to the first half of nodes
13524 break;
13525 }
13526
13527 } // Copy the first half of nodes
13528
13529 // The second half
13530
13531 // Add the first node of the second half
13532 second_half_node_pt.push_back(*it_sub);
13533
13534 // Skip the first node of the second half
13535 it_sub++;
13536
13537 // Loop to copy the nodes
13538 for (; it_sub != tmp_sub_nodes[0].end(); it_sub++)
13539 {
13540 // Add the sub-node to the first half
13541 second_half_node_pt.push_back(*it_sub);
13542
13543 // Add the boundary elements of the first half
13544 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[0][counter2]);
13545 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[0][counter2 + 1]);
13546 // Add the face indexes of the first half
13547 second_half_face_idx.push_back(tmp_sub_face_idx_ele[0][counter2]);
13548 second_half_face_idx.push_back(tmp_sub_face_idx_ele[0][counter2 + 1]);
13549
13550 // Increase the other counter
13551 counter2 += 2;
13552
13553 } // Copy the second half of nodes
13554
13555 // Add the sub-list of nodes to the vector of lists of nodes
13556 output_sorted_nodes_pt.push_back(first_half_node_pt);
13557 output_sorted_nodes_pt.push_back(second_half_node_pt);
13558 // Add the sub-vector of elements to the vector of boundary
13559 // elements
13560 output_boundary_element_pt.push_back(first_half_ele_pt);
13561 output_boundary_element_pt.push_back(second_half_ele_pt);
13562 // Add the sub-vector of face indexes to the vector of face
13563 // indexes
13564 output_face_index_element.push_back(first_half_face_idx);
13565 output_face_index_element.push_back(second_half_face_idx);
13566
13567 // Set the connection flags, change them by the proper connection
13568 // flag
13569
13570 // ----------------------------------------------------------------
13571 // Connections flags for the first half
13572
13573 // The left connection flag
13574
13575 // Connected with nothing but required to stop adding nodes
13576 if (input_connect_to_the_left == -3)
13577 {
13578 // Set connected to nothing
13579 output_connect_to_the_left.push_back(-1);
13580 }
13581 // Connected with itself
13582 else if (input_connect_to_the_left == -2)
13583 {
13584 // Set connected to nothing, this is the base node
13585 output_connect_to_the_left.push_back(-1);
13586 }
13587 else
13588 {
13589 // Any other value keep it
13590 output_connect_to_the_left.push_back(input_connect_to_the_left);
13591 }
13592
13593 // The right connection flag
13594
13595 // Set connected to nothing, this is the base node
13596 output_connect_to_the_right.push_back(-1);
13597
13598 // Increase the shared boundary id
13599 tmp_shd_bnd_id++;
13600
13601 // ----------------------------------------------------------------
13602 // Connections flags for the second half
13603
13604 // The left connection flag
13605
13606 // Set connected to the previous boundary
13607 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
13608
13609 // The right connection flag
13610
13611 // Are we in the last sub-list of nodes, if that is the case we
13612 // need to respect the flag assigned to the right
13613 if (n_sub_list == 1)
13614 {
13615 if (input_connect_to_the_right == -3)
13616 {
13617 // Set connected to the previous shared boundary id
13618 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13619 }
13620 else if (input_connect_to_the_right == -2)
13621 {
13622 // Set connected to the previous shared boundary id
13623 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13624 }
13625 else if (input_connect_to_the_right == -1)
13626 {
13627 // Set connected to the previous shared boundary id
13628 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13629 }
13630 else
13631 {
13632 // Any other value keep it
13633 output_connect_to_the_right.push_back(input_connect_to_the_right);
13634 }
13635 } // if (n_sub_list == 1)
13636 else
13637 {
13638 // Set connected to the previous shared boundary id
13639 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13640 }
13641
13642 // Increase the shared boundary id
13643 tmp_shd_bnd_id++;
13644
13645 } // if (left_sub_node_pt == right_sub_node_pt)
13646 else
13647 {
13648 // No need to create two boundaries, create only one with the
13649 // sub-list of nodes
13650
13651 // Add the sub-list of nodes to the vector of lists of nodes
13652 output_sorted_nodes_pt.push_back(tmp_sub_nodes[0]);
13653 // Add the sub-vector of elements to the vector of boundary
13654 // elements
13655 output_boundary_element_pt.push_back(tmp_sub_bnd_ele_pt[0]);
13656 // Add the sub-vector of face indexes to the vector of face
13657 // indexes
13658 output_face_index_element.push_back(tmp_sub_face_idx_ele[0]);
13659
13660 // Set the connection flags, change them by the proper connection
13661 // flag
13662
13663 // The left connection flag
13664
13665 // Connected with nothing but required to stop adding nodes
13666 if (input_connect_to_the_left == -3)
13667 {
13668 // Set to connected to nothing
13669 output_connect_to_the_left.push_back(-1);
13670 }
13671 // Connected with itself
13672 else if (input_connect_to_the_left == -2)
13673 {
13674 // Set connected to the next shared polyline id
13675 output_connect_to_the_left.push_back(tmp_shd_bnd_id + 1);
13676 }
13677 else
13678 {
13679 // Any other value keep it
13680 output_connect_to_the_left.push_back(input_connect_to_the_left);
13681 }
13682
13683 // The right connection flag
13684
13685 // Set connected to the next shared polyline id
13686 output_connect_to_the_right.push_back(tmp_shd_bnd_id + 1);
13687
13688 // Increase the shared boundary id by one
13689 tmp_shd_bnd_id++;
13690
13691 } // else if (left_sub_node_pt == right_sub_node_pt)
13692
13693 // At least two sub-list of nodes were created
13694 if (n_sub_list > 1)
13695 {
13696 // ------------------------------------------------------------
13697 // Get the left and right node of the second sub-list of nodes
13698 left_sub_node_pt = tmp_sub_nodes[1].front();
13699 right_sub_node_pt = tmp_sub_nodes[1].back();
13700
13701 // Check if the sub-list of nodes creates a loop (circle)
13702 if (left_sub_node_pt == right_sub_node_pt)
13703 {
13704 // We need to create two shared polylines and therefore increase
13705 // the shared boundary id by two
13706
13707 // The first and second half of nodes
13708 std::list<Node*> first_half_node_pt;
13709 std::list<Node*> second_half_node_pt;
13710 // The first and second half of boundary elements
13711 Vector<FiniteElement*> first_half_ele_pt;
13712 Vector<FiniteElement*> second_half_ele_pt;
13713 // The first and second half of face indexes
13714 Vector<int> first_half_face_idx;
13715 Vector<int> second_half_face_idx;
13716
13717 // Get the number of sub-nodes in the sub-list of nodes
13718 const unsigned n_sub_nodes = tmp_sub_nodes[1].size();
13719
13720 // The number of sub-nodes for the first half of the shared
13721 // boundary
13722 const unsigned n_sub_nodes_half =
13723 static_cast<unsigned>(n_sub_nodes / 2.0);
13724
13725 // Copy as many sub-nodes for the first half of the sub-polyline
13726
13727 // Iterator to loop over the nodes
13728 std::list<Node*>::iterator it_sub = tmp_sub_nodes[1].begin();
13729
13730 // Add the first node
13731 first_half_node_pt.push_back(*it_sub);
13732
13733 // Skip the first node
13734 it_sub++;
13735
13736 // Counter
13737 unsigned counter_nodes = 0;
13738 unsigned counter2 = 0;
13739
13740 // Loop to copy the nodes
13741 for (; it_sub != tmp_sub_nodes[1].end(); it_sub++)
13742 {
13743 // Add the sub-node to the first half
13744 first_half_node_pt.push_back(*it_sub);
13745 // Add the boundary elements of the first half
13746 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[1][counter2]);
13747 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[1][counter2 + 1]);
13748 // Add the face indexes of the first half
13749 first_half_face_idx.push_back(tmp_sub_face_idx_ele[1][counter2]);
13750 first_half_face_idx.push_back(tmp_sub_face_idx_ele[1][counter2 + 1]);
13751
13752 // Increase the counter of added nodes
13753 counter_nodes++;
13754
13755 // Increase the other counter
13756 counter2 += 2;
13757
13758 if (counter_nodes == n_sub_nodes_half)
13759 {
13760 // Stop adding to the first half of nodes
13761 break;
13762 }
13763
13764 } // Copy the first half of nodes
13765
13766 // The second half
13767
13768 // Add the first node of the second half
13769 second_half_node_pt.push_back(*it_sub);
13770
13771 // Skip the first node of the second half
13772 it_sub++;
13773
13774 // Loop to copy the nodes
13775 for (; it_sub != tmp_sub_nodes[1].end(); it_sub++)
13776 {
13777 // Add the sub-node to the first half
13778 second_half_node_pt.push_back(*it_sub);
13779 // Add the boundary elements of the first half
13780 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[1][counter2]);
13781 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[1][counter2 + 1]);
13782 // Add the face indexes of the first half
13783 second_half_face_idx.push_back(tmp_sub_face_idx_ele[1][counter2]);
13784 second_half_face_idx.push_back(tmp_sub_face_idx_ele[1][counter2 + 1]);
13785
13786 // Increase the other counter
13787 counter2 += 2;
13788
13789 } // Copy the second half of nodes
13790
13791 // Add the sub-list of nodes to the vector of lists of nodes
13792 output_sorted_nodes_pt.push_back(first_half_node_pt);
13793 output_sorted_nodes_pt.push_back(second_half_node_pt);
13794 // Add the sub-vector of elements to the vector of boundary
13795 // elements
13796 output_boundary_element_pt.push_back(first_half_ele_pt);
13797 output_boundary_element_pt.push_back(second_half_ele_pt);
13798 // Add the sub-vector of face indexes to the vector of face
13799 // indexes
13800 output_face_index_element.push_back(first_half_face_idx);
13801 output_face_index_element.push_back(second_half_face_idx);
13802
13803 // Set the connection flags, change them by the proper
13804 // connection flag
13805
13806 // --------------------------------------
13807 // Connections flags for the first half
13808
13809 // The left connection flag
13810
13811 // Connected to the previous boundary
13812 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
13813
13814 // The right connection flag
13815
13816 // Set connected to nothing, this is the base node
13817 output_connect_to_the_right.push_back(-1);
13818
13819 // Increase the shared boundary id
13820 tmp_shd_bnd_id++;
13821
13822 // --------------------------------------
13823 // Connections flags for the second half
13824
13825 // The left connection flag
13826
13827 // Set connected to the previous boundary
13828 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
13829
13830 // The right connection flag
13831
13832 // Are we in the last sub-list of nodes, if that is the case we
13833 // need to respect the flag assigned to the right
13834 if (n_sub_list == 2)
13835 {
13836 // Connected with nothing
13837 if (input_connect_to_the_right == -1)
13838 {
13839 // Set connected to the previous shared boundary
13840 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13841 }
13842 // Connected with the same boundary
13843 else if (input_connect_to_the_right == -2)
13844 {
13845 // Set connected to the previous shared boundary
13846 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13847 }
13848 // Connetted with nothing but stop adding nodes
13849 else if (input_connect_to_the_right == -3)
13850 {
13851 // Set connected to the previous shared boundary
13852 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13853 }
13854 else
13855 {
13856 // Any other value keep it
13857 output_connect_to_the_right.push_back(input_connect_to_the_right);
13858 }
13859
13860 // Increase the shared boundary id
13861 tmp_shd_bnd_id++;
13862
13863 } // if (n_sub_list == 2)
13864#ifdef PARANOID
13865 else
13866 {
13867 std::stringstream error_message;
13868 error_message
13869 << "The second sub-list of nodes creates a loop but this is not\n"
13870 << "the last list of sub-nodes.\n"
13871 << "This configuration is not supported\n";
13872 throw OomphLibError(
13873 error_message.str(),
13874 "TriangleMesh::break_loops_on_shared_polyline_helper()",
13875 OOMPH_EXCEPTION_LOCATION);
13876 }
13877#endif
13878
13879 } // if (left_sub_node_pt == right_sub_node_pt)
13880 else
13881 {
13882 // No need to create two boundaries, create only one with the
13883 // sub-list of nodes
13884
13885 // Add the sub-list of nodes to the vector of lists of nodes
13886 output_sorted_nodes_pt.push_back(tmp_sub_nodes[1]);
13887 // Add the sub-vector of elements to the vector of boundary
13888 // elements
13889 output_boundary_element_pt.push_back(tmp_sub_bnd_ele_pt[1]);
13890 // Add the sub-vector of face indexes to the vector of face
13891 // indexes
13892 output_face_index_element.push_back(tmp_sub_face_idx_ele[1]);
13893
13894 // Set the connection flags, change them by the proper connection
13895 // flag
13896
13897 // The left connection flag
13898
13899 // Set connected to the previous shared boundary id
13900 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
13901
13902 // The right connection flag
13903
13904 // Are we in the last sub-list of nodes, if that is the case we
13905 // need to respect the flag assigned to the right
13906 if (n_sub_list == 2)
13907 {
13908 // Connected with nothing but required to stop adding nodes
13909 if (input_connect_to_the_right == -3)
13910 {
13911 // Set to connected to nothing
13912 output_connect_to_the_right.push_back(-1);
13913 }
13914#ifdef PARANOID
13915 // Connected with itself
13916 else if (input_connect_to_the_right == -2)
13917 {
13918 std::stringstream error_message;
13919 error_message
13920 << "The connection flag to the right ("
13921 << input_connect_to_the_right << ") indicates a connection\n"
13922 << "with the same polyline.\n However, the second sub-list of\n"
13923 << "nodes was found not making a loop so no connection with\n"
13924 << "itself should be marked\n\n";
13925 throw OomphLibError(
13926 error_message.str(),
13927 "TriangleMesh::break_loops_on_shared_polyline_helper()",
13928 OOMPH_EXCEPTION_LOCATION);
13929 }
13930#endif
13931 else
13932 {
13933 // Any other value keep it
13934 output_connect_to_the_right.push_back(input_connect_to_the_right);
13935 }
13936 } // if (n_sub_list == 2)
13937 else
13938 {
13939 // Set connected to the next shared boundary id
13940 output_connect_to_the_right.push_back(tmp_shd_bnd_id + 1);
13941 } // else if (n_sub_list == 2)
13942
13943 // Increase the shared boundary id by one
13944 tmp_shd_bnd_id++;
13945
13946 } // if (left_sub_node_pt == right_sub_node_pt)
13947
13948 } // if (n_sub_list > 1)
13949
13950 // Three sub-list of nodes were created
13951 if (n_sub_list > 2)
13952 {
13953 // ------------------------------------------------------------
13954 // Get the left and right node of the third sub-list of nodes
13955 left_sub_node_pt = tmp_sub_nodes[2].front();
13956 right_sub_node_pt = tmp_sub_nodes[2].back();
13957
13958 // Check if the sub-list of nodes creates a loop (circle)
13959 if (left_sub_node_pt == right_sub_node_pt)
13960 {
13961 // We need to create two shared polylines and therefore increase
13962 // the shared boundary id by two
13963
13964 // The first and second half of nodes
13965 std::list<Node*> first_half_node_pt;
13966 std::list<Node*> second_half_node_pt;
13967 // The first and second half of boundary elements
13968 Vector<FiniteElement*> first_half_ele_pt;
13969 Vector<FiniteElement*> second_half_ele_pt;
13970 // The first and second half of face indexes
13971 Vector<int> first_half_face_idx;
13972 Vector<int> second_half_face_idx;
13973
13974 // Get the number of sub-nodes in the sub-list of nodes
13975 const unsigned n_sub_nodes = tmp_sub_nodes[2].size();
13976
13977 // The number of sub-nodes for the first half of the shared
13978 // boundary
13979 const unsigned n_sub_nodes_half =
13980 static_cast<unsigned>(n_sub_nodes / 2.0);
13981
13982 // Copy as many sub-nodes for the first half of the sub-polyline
13983
13984 // Iterator to loop over the nodes
13985 std::list<Node*>::iterator it_sub = tmp_sub_nodes[2].begin();
13986
13987 // Add the first node
13988 first_half_node_pt.push_back(*it_sub);
13989
13990 // Skip the first node
13991 it_sub++;
13992
13993 // Counter
13994 unsigned counter_nodes = 0;
13995 unsigned counter2 = 0;
13996
13997 // Loop to copy the nodes
13998 for (; it_sub != tmp_sub_nodes[2].end(); it_sub++)
13999 {
14000 // Add the sub-node to the first half
14001 first_half_node_pt.push_back(*it_sub);
14002 // Add the boundary elements of the first half
14003 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[2][counter2]);
14004 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[2][counter2 + 1]);
14005 // Add the face indexes of the first half
14006 first_half_face_idx.push_back(tmp_sub_face_idx_ele[2][counter2]);
14007 first_half_face_idx.push_back(tmp_sub_face_idx_ele[2][counter2 + 1]);
14008
14009 // Increase the counter of added nodes
14010 counter_nodes++;
14011
14012 // Increase the other counter
14013 counter2 += 2;
14014
14015 if (counter_nodes == n_sub_nodes_half)
14016 {
14017 // Stop adding to the first half of nodes
14018 break;
14019 }
14020
14021 } // Copy the first half of nodes
14022
14023 // The second half
14024
14025 // Add the first node of the second half
14026 second_half_node_pt.push_back(*it_sub);
14027
14028 // Skip the first node of the second half
14029 it_sub++;
14030
14031 // Loop to copy the nodes
14032 for (; it_sub != tmp_sub_nodes[2].end(); it_sub++)
14033 {
14034 // Add the sub-node to the first half
14035 second_half_node_pt.push_back(*it_sub);
14036 // Add the boundary elements of the first half
14037 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[2][counter2]);
14038 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[2][counter2 + 1]);
14039 // Add the face indexes of the first half
14040 second_half_face_idx.push_back(tmp_sub_face_idx_ele[2][counter2]);
14041 second_half_face_idx.push_back(tmp_sub_face_idx_ele[2][counter2 + 1]);
14042
14043 // Increase the other counter
14044 counter2 += 2;
14045
14046 } // Copy the second half of nodes
14047
14048 // Add the sub-list of nodes to the vector of lists of nodes
14049 output_sorted_nodes_pt.push_back(first_half_node_pt);
14050 output_sorted_nodes_pt.push_back(second_half_node_pt);
14051 // Add the sub-vector of elements to the vector of boundary
14052 // elements
14053 output_boundary_element_pt.push_back(first_half_ele_pt);
14054 output_boundary_element_pt.push_back(second_half_ele_pt);
14055 // Add the sub-vector of face indexes to the vector of face
14056 // indexes
14057 output_face_index_element.push_back(first_half_face_idx);
14058 output_face_index_element.push_back(second_half_face_idx);
14059
14060 // --------------------------------------
14061 // Connections flags for the first half
14062
14063 // The left connection flag
14064
14065 // Connected to the previous shared boundary
14066 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
14067
14068 // The right connection flag
14069
14070 // Set connected to nothing, this is the base node
14071 output_connect_to_the_right.push_back(-1);
14072
14073 // Increase the shared boundary id
14074 tmp_shd_bnd_id++;
14075
14076 // --------------------------------------
14077 // Connections flags for the second half
14078
14079 // The left connection flag
14080
14081 // Set connected to the previous boundary
14082 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
14083
14084 // The right connection flag
14085
14086 if (input_connect_to_the_right == -3)
14087 {
14088 // Set connected to the previous shared boundary id
14089 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14090 }
14091 else if (input_connect_to_the_right == -2)
14092 {
14093 // Set connected to the previous shared boundary id
14094 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14095 }
14096 else if (input_connect_to_the_right == -1)
14097 {
14098 // Set connected to the previous shared boundary id
14099 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14100 }
14101 else
14102 {
14103 // Any other value keep it
14104 output_connect_to_the_right.push_back(input_connect_to_the_right);
14105 }
14106
14107 // Increase the shared boundary id
14108 tmp_shd_bnd_id++;
14109
14110 } // if (left_sub_node_pt == right_sub_node_pt)
14111 else
14112 {
14113 // No need to create two boundaries, create only one with the
14114 // sub-list of nodes
14115
14116 // Add the sub-list of nodes to the vector of lists of nodes
14117 output_sorted_nodes_pt.push_back(tmp_sub_nodes[2]);
14118 // Add the sub-vector of elements to the vector of boundary
14119 // elements
14120 output_boundary_element_pt.push_back(tmp_sub_bnd_ele_pt[2]);
14121 // Add the sub-vector of face indexes to the vector of face
14122 // indexes
14123 output_face_index_element.push_back(tmp_sub_face_idx_ele[2]);
14124
14125 // Set the connection flags, change them by the proper
14126 // connection flag
14127
14128 // The left connection flag
14129
14130 // Set connected to the previous shared boundary id
14131 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
14132
14133 // The right connection flag
14134
14135 // Connected with nothing but required to stop adding nodes
14136 if (input_connect_to_the_right == -3)
14137 {
14138 std::stringstream error_message;
14139 error_message
14140 << "The connection flag to the right ("
14141 << input_connect_to_the_right << ") indicates 'no connection and\n"
14142 << "stop adding nodes'.\n However, the thrid sub-list of\n"
14143 << "nodes must have a connection to the right with the same\n"
14144 << "shared polyline or with any other polyline\n\n";
14145 throw OomphLibError(
14146 error_message.str(),
14147 "TriangleMesh::break_loops_on_shared_polyline_helper()",
14148 OOMPH_EXCEPTION_LOCATION);
14149 }
14150 else if (input_connect_to_the_right == -1)
14151 {
14152 std::stringstream error_message;
14153 error_message
14154 << "The connection flag to the right ("
14155 << input_connect_to_the_right << ") indicates 'no connection.\n"
14156 << "However, the thrid sub-list of nodes must have a connection\n"
14157 << "to the right with the same shared polyline or with any other\n"
14158 << "polyline\n\n";
14159 throw OomphLibError(
14160 error_message.str(),
14161 "TriangleMesh::break_loops_on_shared_polyline_helper()",
14162 OOMPH_EXCEPTION_LOCATION);
14163 }
14164 // Connected with itself
14165 else if (input_connect_to_the_right == -2)
14166 {
14167 // Set connected to the previous shared boundary id
14168 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14169 }
14170 else
14171 {
14172 // Any other value keep it
14173 output_connect_to_the_right.push_back(input_connect_to_the_right);
14174 }
14175
14176 // Increase the shared boundary id by one
14177 tmp_shd_bnd_id++;
14178
14179 } // if (left_sub_node_pt == right_sub_node_pt)
14180
14181 } // if (n_sub_list > 2)
14182 }
14183
14184 // ======================================================================
14185 // Break any possible loop created by the sorted list of nodes
14186 // that is used to create a new shared polyline
14187 // ======================================================================
14188 template<class ELEMENT>
14191 const unsigned& initial_shd_bnd_id,
14192 std::list<Node*>& input_nodes,
14193 Vector<FiniteElement*>& input_boundary_element_pt,
14194 Vector<FiniteElement*>& input_boundary_face_element_pt,
14195 Vector<int>& input_face_index_element,
14196 const int& input_connect_to_the_left,
14197 const int& input_connect_to_the_right,
14198 Vector<std::list<Node*>>& output_sorted_nodes_pt,
14199 Vector<Vector<FiniteElement*>>& output_boundary_element_pt,
14200 Vector<Vector<FiniteElement*>>& output_boundary_face_element_pt,
14201 Vector<Vector<int>>& output_face_index_element,
14202 Vector<int>& output_connect_to_the_left,
14203 Vector<int>& output_connect_to_the_right)
14204 {
14205 // Get the left and right node of the current list of sorted nodes
14206 Node* left_node_pt = input_nodes.front();
14207 Node* right_node_pt = input_nodes.back();
14208
14209 // Temporary storage for list of nodes, boundary elements, boundary
14210 // face elements and face element's indexes
14211 Vector<std::list<Node*>> tmp_sub_nodes;
14212 Vector<Vector<FiniteElement*>> tmp_sub_bnd_ele_pt;
14213 Vector<Vector<FiniteElement*>> tmp_sub_bnd_face_ele_pt;
14214 Vector<Vector<int>> tmp_sub_face_idx_ele;
14215
14216 // Iterator for the list of input nodes
14217 std::list<Node*>::iterator it = input_nodes.begin();
14218
14219 // Counter
14220 unsigned counter = 0;
14221
14222 // Loop while not all nodes have been done
14223 while (it != input_nodes.end())
14224 {
14225 // Check if the current node is the final one
14226 it++;
14227 // Is the current node the final node?
14228 if (it == input_nodes.end())
14229 {
14230 // Break, add no more nodes
14231 break;
14232 }
14233 else
14234 {
14235 // Restore the iterator
14236 it--;
14237 }
14238
14239 // Get a list of nonrepeated nodes
14240 std::list<Node*> sub_nodes;
14241 // The temporary vector of boundary elements associated with the
14242 // nodes
14243 Vector<FiniteElement*> sub_bnd_ele_pt;
14244 // The temporary vector of boundary face elements associated with
14245 // the nodes
14246 Vector<FiniteElement*> sub_bnd_face_ele_pt;
14247 // The temporary vector of face indexes associated with the
14248 // boundary elements
14249 Vector<int> sub_face_idx_ele;
14250
14251 // Add the current node to the list
14252 sub_nodes.push_back(*it);
14253
14254 // Add nodes until found a repeated node (the left or right
14255 // node) or until reaching the end of the list of nodes
14256 do
14257 {
14258 // Go to the next node
14259 ++it;
14260
14261 // Add the new node
14262 sub_nodes.push_back((*it));
14263
14264 // Add the boundary elements
14265 sub_bnd_ele_pt.push_back(input_boundary_element_pt[counter]);
14266
14267 // Add the boundary face elements
14268 sub_bnd_face_ele_pt.push_back(input_boundary_face_element_pt[counter]);
14269
14270 // Add the face indexes
14271 sub_face_idx_ele.push_back(input_face_index_element[counter]);
14272
14273 // Increase the counter
14274 counter++;
14275
14276 // Continue adding until reaching a repeated node or the end
14277 // of the list of nodes
14278 } while ((*it) != left_node_pt && (*it) != right_node_pt &&
14279 it != input_nodes.end());
14280
14281 // Add the sub-set of nodes to the temporary storage
14282 tmp_sub_nodes.push_back(sub_nodes);
14283
14284 // Add the boundary elements to the temporary storage
14285 tmp_sub_bnd_ele_pt.push_back(sub_bnd_ele_pt);
14286 // Add the boundary face elements to the temporary storage
14287 tmp_sub_bnd_face_ele_pt.push_back(sub_bnd_face_ele_pt);
14288 // Add the face indexes to the temporary storage
14289 tmp_sub_face_idx_ele.push_back(sub_face_idx_ele);
14290
14291 } // while((*it) != input_nodes.end())
14292
14293 // --------------------------------------------------
14294 // Now create as many shared boundaries as required
14295
14296 // Get the number of sub-list of nodes created
14297 const unsigned n_sub_list = tmp_sub_nodes.size();
14298
14299#ifdef PARANOID
14300 if (n_sub_list > 1)
14301 {
14302 std::stringstream error_message;
14303 error_message
14304 << "The number of sub-list of nodes created from the shared\n"
14305 << "polyline with loops was (" << n_sub_list << ").\n"
14306 << "We can only handle one list which may still contain loops\n"
14307 << "(or repeated nodes)\n";
14308 throw OomphLibError(
14309 error_message.str(),
14310 "TriangleMesh::break_loops_on_shared_polyline_load_balance_helper()",
14311 OOMPH_EXCEPTION_LOCATION);
14312 }
14313#endif
14314
14315 // If there is only one list it may be because there are no loops or
14316 // there is only one loop (a circle)
14317 if (n_sub_list == 1 && (left_node_pt != right_node_pt))
14318 {
14319 // There are no loops, return just after filling the data
14320 // structures
14321
14322 // This is the base case used most of the times
14323
14324 // Set the vector of lists of nodes
14325 output_sorted_nodes_pt = tmp_sub_nodes;
14326 // Set the vector of boundary elements
14327 output_boundary_element_pt = tmp_sub_bnd_ele_pt;
14328 // Set the vector of boundary face elements
14329 output_boundary_face_element_pt = tmp_sub_bnd_face_ele_pt;
14330 // Set the vector of face indexes
14331 output_face_index_element = tmp_sub_face_idx_ele;
14332
14333 // Set the connection flags, change them by the proper connection
14334 // flag
14335
14336#ifdef PARANOID
14337 if (input_connect_to_the_left == -2)
14338 {
14339 std::stringstream error_message;
14340 error_message
14341 << "The connection flag to the left (" << input_connect_to_the_left
14342 << ") indicates a connection\n"
14343 << "with the same polyline.\n However, only one sub-polyline was "
14344 << "found and no loops\nwere identified\n\n";
14345 throw OomphLibError(
14346 error_message.str(),
14347 "TriangleMesh::break_loops_on_shared_polyline_load_balance_helper()",
14348 OOMPH_EXCEPTION_LOCATION);
14349 }
14350#endif
14351
14352 // The left connection flag
14353 if (input_connect_to_the_left == -3)
14354 {
14355 output_connect_to_the_left.push_back(-1);
14356 }
14357 else
14358 {
14359 output_connect_to_the_left.push_back(input_connect_to_the_left);
14360 }
14361
14362#ifdef PARANOID
14363 if (input_connect_to_the_right == -2)
14364 {
14365 std::stringstream error_message;
14366 error_message
14367 << "The connection flag to the right (" << input_connect_to_the_right
14368 << ") indicates a connection\n"
14369 << "with the same polyline.\n However, only one sub-polyline was "
14370 << "found and no loops\nwere identified\n\n";
14371 throw OomphLibError(
14372 error_message.str(),
14373 "TriangleMesh::break_loops_on_shared_polyline_load_balance_helper()",
14374 OOMPH_EXCEPTION_LOCATION);
14375 }
14376#endif
14377
14378 // The right connection flag
14379 if (input_connect_to_the_right == -3)
14380 {
14381 output_connect_to_the_right.push_back(-1);
14382 }
14383 else
14384 {
14385 output_connect_to_the_right.push_back(input_connect_to_the_right);
14386 }
14387
14388 // Return immediately
14389 return;
14390 }
14391
14392 // The temporary storage for the shared boundary id
14393 unsigned tmp_shd_bnd_id = initial_shd_bnd_id;
14394
14395 // -----------------------------------------------------------------
14396 // Check all the sub-list of nodes and create two shared boundaries
14397 // from those that make a loop (circle)
14398
14399 // -----------------------------------------------------------
14400 // Get the left and right node of the first sub-list of nodes
14401 Node* left_sub_node_pt = tmp_sub_nodes[0].front();
14402 Node* right_sub_node_pt = tmp_sub_nodes[0].back();
14403
14404 // Check if the sub-list of nodes creates a loop (circle)
14405 if (left_sub_node_pt == right_sub_node_pt)
14406 {
14407 // We need to create two shared polylines and therefore increase
14408 // the shared boundary id by two
14409
14410 // The first and second half of nodes
14411 std::list<Node*> first_half_node_pt;
14412 std::list<Node*> second_half_node_pt;
14413 // The first and second half of boundary elements
14414 Vector<FiniteElement*> first_half_ele_pt;
14415 Vector<FiniteElement*> second_half_ele_pt;
14416 // The first and second half of boundary face elements
14417 Vector<FiniteElement*> first_half_ele_face_pt;
14418 Vector<FiniteElement*> second_half_ele_face_pt;
14419 // The first and second half of face indexes
14420 Vector<int> first_half_face_idx;
14421 Vector<int> second_half_face_idx;
14422
14423 // Get the number of sub-nodes in the sub-list of nodes
14424 const unsigned n_sub_nodes = tmp_sub_nodes[0].size();
14425
14426 // The number of sub-nodes for the first half of the shared
14427 // boundary
14428 const unsigned n_sub_nodes_half =
14429 static_cast<unsigned>(n_sub_nodes / 2.0);
14430
14431 // Copy as many sub-nodes for the first half of the sub-polyline
14432
14433 // Iterator to loop over the nodes
14434 std::list<Node*>::iterator it_sub = tmp_sub_nodes[0].begin();
14435
14436 // Add the first node
14437 first_half_node_pt.push_back(*it_sub);
14438
14439 // Skip the first node
14440 it_sub++;
14441
14442 // Counter
14443 unsigned counter_nodes = 0;
14444 unsigned counter2 = 0;
14445
14446 // Loop to copy the nodes
14447 for (; it_sub != tmp_sub_nodes[0].end(); it_sub++)
14448 {
14449 // Add the sub-node to the first half
14450 first_half_node_pt.push_back(*it_sub);
14451
14452 // Add the boundary elements of the first half
14453 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[0][counter2]);
14454 // Add the boundary face elements of the first half
14455 first_half_ele_face_pt.push_back(tmp_sub_bnd_face_ele_pt[0][counter2]);
14456 // Add the face indexes of the first half
14457 first_half_face_idx.push_back(tmp_sub_face_idx_ele[0][counter2]);
14458
14459 // Increase the counter of added nodes
14460 counter_nodes++;
14461
14462 // Increase the other counter (of the elements/face)
14463 counter2++;
14464
14465 if (counter_nodes == n_sub_nodes_half)
14466 {
14467 // Stop adding to the first half of nodes
14468 break;
14469 }
14470
14471 } // Copy the first half of nodes
14472
14473 // The second half
14474
14475 // Add the first node of the second half
14476 second_half_node_pt.push_back(*it_sub);
14477
14478 // Skip the first node of the second half
14479 it_sub++;
14480
14481 // Loop to copy the nodes
14482 for (; it_sub != tmp_sub_nodes[0].end(); it_sub++)
14483 {
14484 // Add the sub-node to the first half
14485 second_half_node_pt.push_back(*it_sub);
14486
14487 // Add the boundary elements of the first half
14488 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[0][counter2]);
14489 // Add the boundary face elements of the first half
14490 second_half_ele_face_pt.push_back(tmp_sub_bnd_face_ele_pt[0][counter2]);
14491 // Add the face indexes of the first half
14492 second_half_face_idx.push_back(tmp_sub_face_idx_ele[0][counter2]);
14493
14494 // Increase the other counter
14495 counter2++;
14496
14497 } // Copy the second half of nodes
14498
14499 // Add the sub-list of nodes to the vector of lists of nodes
14500 output_sorted_nodes_pt.push_back(first_half_node_pt);
14501 output_sorted_nodes_pt.push_back(second_half_node_pt);
14502 // Add the sub-vector of elements to the vector of boundary
14503 // elements
14504 output_boundary_element_pt.push_back(first_half_ele_pt);
14505 output_boundary_element_pt.push_back(second_half_ele_pt);
14506 // Add the sub-vector of face elements to the vector of boundary
14507 // elements
14508 output_boundary_face_element_pt.push_back(first_half_ele_face_pt);
14509 output_boundary_face_element_pt.push_back(second_half_ele_face_pt);
14510 // Add the sub-vector of face indexes to the vector of face
14511 // indexes
14512 output_face_index_element.push_back(first_half_face_idx);
14513 output_face_index_element.push_back(second_half_face_idx);
14514
14515 // Set the connection flags, change them by the proper connection
14516 // flag
14517
14518 // ----------------------------------------------------------------
14519 // Connections flags for the first half
14520
14521 // The left connection flag
14522
14523 // Connected with nothing but required to stop adding nodes
14524 if (input_connect_to_the_left == -3)
14525 {
14526 // Set connected to nothing
14527 output_connect_to_the_left.push_back(-1);
14528 }
14529 // Connected with itself
14530 else if (input_connect_to_the_left == -2)
14531 {
14532 // Set connected to nothing, this is the base node
14533 output_connect_to_the_left.push_back(-1);
14534 }
14535 else
14536 {
14537 // Any other value keep it
14538 output_connect_to_the_left.push_back(input_connect_to_the_left);
14539 }
14540
14541 // The right connection flag
14542
14543 // Set connected to nothing, this is the base node
14544 output_connect_to_the_right.push_back(-1);
14545
14546 // Increase the shared boundary id
14547 tmp_shd_bnd_id++;
14548
14549 // ----------------------------------------------------------------
14550 // Connections flags for the second half
14551
14552 // The left connection flag
14553
14554 // Set connected to the previous boundary
14555 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
14556
14557 // The right connection flag
14558
14559 // Are we in the last sub-list of nodes, if that is the case we
14560 // need to respect the flag assigned to the right
14561 if (n_sub_list == 1)
14562 {
14563 if (input_connect_to_the_right == -3)
14564 {
14565 // Set connected to the previous shared boundary id
14566 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14567 }
14568 else if (input_connect_to_the_right == -2)
14569 {
14570 // Set connected to the previous shared boundary id
14571 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14572 }
14573 else if (input_connect_to_the_right == -1)
14574 {
14575 // Set connected to the previous shared boundary id
14576 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14577 }
14578 else
14579 {
14580 // Any other value keep it
14581 output_connect_to_the_right.push_back(input_connect_to_the_right);
14582 }
14583 } // if (n_sub_list == 1)
14584 else
14585 {
14586 // Set connected to the previous shared boundary id
14587 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14588 }
14589
14590 // Increase the shared boundary id
14591 tmp_shd_bnd_id++;
14592
14593 } // if (left_sub_node_pt == right_sub_node_pt)
14594#ifdef PARANOID
14595 else
14596 {
14597 std::stringstream error_message;
14598 error_message
14599 << "The initial and final node in the current shared polyline are not\n"
14600 << "the same and the number of sublists is (" << n_sub_list << ").\n"
14601 << "We can not handle more than one sublist in the method to break\n"
14602 << "loops at the load balance stage\n\n";
14603 throw OomphLibError(
14604 error_message.str(),
14605 "TriangleMesh::break_loops_on_shared_polyline_load_balance_helper()",
14606 OOMPH_EXCEPTION_LOCATION);
14607 }
14608#endif
14609 }
14610
14611 // ======================================================================
14612 // Create the shared polyline and fill the data structured
14613 // that keep all the information associated with the creationg of the
14614 // shared boundary
14615 // ======================================================================
14616 template<class ELEMENT>
14618 const unsigned& my_rank,
14619 const unsigned& shd_bnd_id,
14620 const unsigned& iproc,
14621 const unsigned& jproc,
14622 std::list<Node*>& sorted_nodes,
14623 const int& root_edge_bnd_id,
14624 Vector<FiniteElement*>& bulk_bnd_ele_pt,
14625 Vector<int>& face_index_ele,
14626 Vector<Vector<TriangleMeshPolyLine*>>& unsorted_polylines_pt,
14627 const int& connect_to_the_left_flag,
14628 const int& connect_to_the_right_flag)
14629 {
14630 // ----------------------------------------------------------------
14631 // Associate the shared boundary with the respective processors
14632 // ----------------------------------------------------------------
14633
14634 // Setup the global look-up scheme, where all processors know the
14635 // associations of others processors and the shared boundaries they
14636 // created
14637
14638 // Set up the boundary shared by "iproc" with "jproc" processor
14639 Shared_boundaries_ids[iproc][jproc].push_back(shd_bnd_id);
14640
14641 // Set up the boundary shared by "jproc" with "iproc" processor
14642 Shared_boundaries_ids[jproc][iproc].push_back(shd_bnd_id);
14643
14644 // Specify the processors involved on the creation of the shared
14645 // boundary
14646 Vector<unsigned> processors(2);
14647 processors[0] = iproc;
14648 processors[1] = jproc;
14649 Shared_boundary_from_processors[shd_bnd_id] = processors;
14650
14651 // ----------------------------------------------------------------
14652 // If one of the processor associated with the shared boundary is
14653 // the current processor then it needs to create a polyline from the
14654 // input sorted nodes, other processors can skip this part
14655 if (iproc == my_rank || jproc == my_rank)
14656 {
14657 // ------------------------------------------------------------
14658 // Create a vertices representation from the sorted nodes list
14659 // ------------------------------------------------------------
14660
14661 // Get the number of nodes on the list
14662 const unsigned n_nodes = sorted_nodes.size();
14663 // The vector to store the vertices (assign space)
14664 Vector<Vector<double>> vertices(n_nodes);
14665
14666 // Copy the vertices from the nodes
14667 unsigned counter = 0;
14668
14669 for (std::list<Node*>::iterator it = sorted_nodes.begin();
14670 it != sorted_nodes.end();
14671 it++)
14672 {
14673 vertices[counter].resize(2);
14674 vertices[counter][0] = (*it)->x(0);
14675 vertices[counter][1] = (*it)->x(1);
14676 counter++;
14677 }
14678
14679 // ---------------------------------------------
14680 // Create the polyline from the input vertices
14681 // ---------------------------------------------
14682 TriangleMeshPolyLine* polyline_pt =
14683 new TriangleMeshPolyLine(vertices, shd_bnd_id);
14684
14685 // ---------------------------------------------
14686 // Establish the internal boundary information
14687 // ---------------------------------------------
14688
14689 // Check if the shared boundary is overlapping (or is part) of an
14690 // internal boundary
14691 if (root_edge_bnd_id != -1)
14692 {
14693 // If the shared boundary is part of an internal boundary then
14694 // mark the shared boundary
14695 Shared_boundary_overlaps_internal_boundary[shd_bnd_id] =
14696 static_cast<unsigned>(root_edge_bnd_id);
14697 } // if (root_edge_bnd_id != -1)
14698
14699 // ---------------------------------------------
14700 // Store the boundary elements and face indexes
14701 // ---------------------------------------------
14702
14703 // Store the shared boundary elements
14704 const unsigned n_shared_boundary_elements = bulk_bnd_ele_pt.size();
14705#ifdef PARANOID
14706 // Check that the number of shared boundy elements is the same as
14707 // the number of face indexes
14708 const unsigned n_face_index = face_index_ele.size();
14709 if (n_shared_boundary_elements != n_face_index)
14710 {
14711 std::ostringstream error_message;
14712 error_message
14713 << "The number of shared boundary elements is different from the\n"
14714 << "number of face indexes associated to the shared boundary\n"
14715 << "elements\n"
14716 << "Number of shared boundary elements: ("
14717 << n_shared_boundary_elements << ")\n"
14718 << "Number of face indexes: (" << n_face_index << ")\n\n";
14719 throw OomphLibError(error_message.str(),
14720 "TriangleMesh::create_shared_polyline()",
14721 OOMPH_EXCEPTION_LOCATION);
14722 } // if (n_shared_boundary_elements != n_face_index)
14723#endif
14724
14725 // Add the shared boundary elements and their respective face
14726 // indexes to their permanent containers
14727 for (unsigned i = 0; i < n_shared_boundary_elements; i++)
14728 {
14729 add_shared_boundary_element(shd_bnd_id, bulk_bnd_ele_pt[i]);
14730 add_face_index_at_shared_boundary(shd_bnd_id, face_index_ele[i]);
14731 } // for (i < nshared_boundary_elements)
14732
14733 // Store the shared boundary nodes
14734 for (std::list<Node*>::iterator it = sorted_nodes.begin();
14735 it != sorted_nodes.end();
14736 it++)
14737 {
14738 add_shared_boundary_node(shd_bnd_id, (*it));
14739 } // for (it != sorted_nodes.end())
14740
14741 // ----------------------------------------------------------
14742 // Create additional look-up schemes for the shared boundary
14743 // ----------------------------------------------------------
14744
14745 // Updates bnd_id <---> curve section map
14746 this->Boundary_curve_section_pt[shd_bnd_id] = polyline_pt;
14747
14748 // Check the size of the unsorted_polylines_pt structure. This
14749 // will have n_procs = 1 when it was called from the
14750 // create_new_shared_boundaries() methods
14751 const unsigned n_procs = unsorted_polylines_pt.size();
14752 if (n_procs > 1)
14753 {
14754 // Add the new created polyline to the list of unsorted
14755 // polylines
14756 unsorted_polylines_pt[iproc].push_back(polyline_pt);
14757
14758 // ... do this on both processors involved in the creation of
14759 // the shared boundary
14760 unsorted_polylines_pt[jproc].push_back(polyline_pt);
14761 }
14762 else
14763 {
14764 // Add the new created polyline to the list of unsorted
14765 // polylines
14766 unsorted_polylines_pt[0].push_back(polyline_pt);
14767 }
14768
14769 // Mark the polyline for deletion (when calling destructor)
14770 this->Free_curve_section_pt.insert(polyline_pt);
14771
14772 // ----------------------------
14773 // Set connection information
14774 // ----------------------------
14775
14776 // Check that the flags are correct, no connection or the boundary
14777 // id of the boundary to connect
14778#ifdef PARANOID
14779 // Is the shared polyline not connected to the left
14780 if (connect_to_the_left_flag < 0)
14781 {
14782 // If not connected then should be specified by -1
14783 if (connect_to_the_left_flag != -1)
14784 {
14785 std::ostringstream error_message;
14786 error_message
14787 << "The only accepted values for the connection flags are:\n"
14788 << "POSITIVE values or -1, any other value is rejected, please\n"
14789 << "check that you previously called the methods to deal with\n"
14790 << "other flag values\n"
14791 << "The current flag value for connection to the left is: ("
14792 << connect_to_the_left_flag << ")\n\n";
14793 throw OomphLibError(error_message.str(),
14794 "TriangleMesh::create_shared_polyline()",
14795 OOMPH_EXCEPTION_LOCATION);
14796 } // if (connect_to_the_left_flag != -1)
14797 } // if (connect_to_the_left_flag < 0)
14798
14799 // Is the shared polyline not connected to the right
14800 if (connect_to_the_right_flag < 0)
14801 {
14802 // If not connected then should be specified by -1
14803 if (connect_to_the_right_flag != -1)
14804 {
14805 std::ostringstream error_message;
14806 error_message
14807 << "The only accepted values for the connection flags are:\n"
14808 << "POSITIVE values or -1, any other value is rejected, please\n"
14809 << "check that you previously called the methods to deal with\n"
14810 << "other flag values\n"
14811 << "The current flag value for connection to the right is: ("
14812 << connect_to_the_right_flag << ")\n\n";
14813 throw OomphLibError(error_message.str(),
14814 "TriangleMesh::create_shared_polyline()",
14815 OOMPH_EXCEPTION_LOCATION);
14816 } // if (connect_to_the_right_flag != -1)
14817 } // if (connect_to_the_right_flag < 0)
14818#endif
14819
14820 // Set the connection to the left
14821 if (connect_to_the_left_flag != -1)
14822 {
14823 // Get the unsigned version of the boundary id to the left
14824 const unsigned bnd_id_connection_to_the_left =
14825 static_cast<unsigned>(connect_to_the_left_flag);
14826 // Set the initial vertex as connected
14827 polyline_pt->set_initial_vertex_connected();
14828 // Set the initial vertex connected boundary id
14829 polyline_pt->initial_vertex_connected_bnd_id() =
14830 bnd_id_connection_to_the_left;
14831 // Set the chunk number to zero
14832 polyline_pt->initial_vertex_connected_n_chunk() = 0;
14833
14834 } // if (connect_to_the_left_flag != -1)
14835
14836 // Set the connection to the right
14837 if (connect_to_the_right_flag != -1)
14838 {
14839 // Get the unsigned version of the boundary id to the right
14840 const unsigned bnd_id_connection_to_the_right =
14841 static_cast<unsigned>(connect_to_the_right_flag);
14842 // Set the final vertex as connected
14843 polyline_pt->set_final_vertex_connected();
14844 // Set the final vertex connected boundary id
14845 polyline_pt->final_vertex_connected_bnd_id() =
14846 bnd_id_connection_to_the_right;
14847 // Set the chunk number to zero
14848 polyline_pt->final_vertex_connected_n_chunk() = 0;
14849
14850 } // if (connect_to_the_right_flag != -1)
14851
14852 } // if (iproc == my_rank || jproc == my_rank)
14853 }
14854
14855 //======================================================================
14856 /// Reset the boundary elements info. after load balance have
14857 /// taken place
14858 //======================================================================
14859 template<class ELEMENT>
14861 Vector<unsigned>& ntmp_boundary_elements,
14862 Vector<Vector<unsigned>>& ntmp_boundary_elements_in_region,
14863 Vector<FiniteElement*>& deleted_elements)
14864 {
14865 // Get the number of boundaries
14866 const unsigned nbound = this->nboundary();
14867
14868 // Are there regions?
14869 const unsigned n_regions = this->nregion();
14870
14871 // Loop over the boundaries
14872 for (unsigned b = 0; b < nbound; b++)
14873 {
14874 // Get the boundary elements and back them up
14875 // -----------------------------------------------------------------
14876 // Get the number of boundary elements (mixed with the old and new)
14877 const unsigned nbound_ele = this->nboundary_element(b);
14878 // Back-up the boundary elements
14879 Vector<FiniteElement*> backed_up_boundary_element_pt(nbound_ele);
14880 Vector<int> backed_up_face_index_at_boundary(nbound_ele);
14881 for (unsigned e = 0; e < nbound_ele; e++)
14882 {
14883 // Get the old boundary element
14884 backed_up_boundary_element_pt[e] = this->boundary_element_pt(b, e);
14885 // Get the old face index
14886 backed_up_face_index_at_boundary[e] =
14887 this->face_index_at_boundary(b, e);
14888 } // for (n < nold_boundary_elements)
14889
14890 // Back up the elements in boundary for each region
14891 Vector<Vector<FiniteElement*>> backed_up_boundary_region_element_pt(
14892 n_regions);
14893 Vector<Vector<int>> backed_up_face_index_at_boundary_region(n_regions);
14894
14895 // Loop over the regions and back up the boundary elements in
14896 // regions
14897 for (unsigned ir = 0; ir < n_regions; ir++)
14898 {
14899 // Get the region id
14900 const unsigned region_id =
14901 static_cast<unsigned>(this->region_attribute(ir));
14902 // Get the number of boundary region elements (mixed old and new)
14903 const unsigned nbnd_region_ele =
14904 this->nboundary_element_in_region(b, region_id);
14905
14906 // Loop over the elements in the region
14907 for (unsigned e = 0; e < nbnd_region_ele; e++)
14908 {
14909 // Get the old boundary region element
14910 backed_up_boundary_region_element_pt[ir][e] =
14911 this->boundary_element_in_region_pt(b, region_id, e);
14912
14913 // Get the old face index
14914 backed_up_face_index_at_boundary_region[ir][e] =
14915 this->face_index_at_boundary_in_region(b, region_id, e);
14916 } // for (e < nbnd_region_ele)
14917
14918 } // for (ir < n_regions)
14919
14920 // Clean all previous storages
14921 this->Boundary_element_pt[b].clear();
14922 this->Face_index_at_boundary[b].clear();
14923 if (n_regions > 0)
14924 {
14925 this->Boundary_region_element_pt[b].clear();
14926 this->Face_index_region_at_boundary[b].clear();
14927 }
14928
14929 // -------------------------------------------------------------------
14930 // Now copy only the elements that are still alive, from those before
14931 // the re-establishment of halo and haloed elements
14932 // -------------------------------------------------------------------
14933 // Start with the boundary elements
14934 // Get the old number of boundary elements
14935 const unsigned nold_bnd_ele = ntmp_boundary_elements[b];
14936 // Loop over the boundary elements and check those still alive
14937 for (unsigned e = 0; e < nold_bnd_ele; e++)
14938 {
14939 FiniteElement* tmp_ele_pt = backed_up_boundary_element_pt[e];
14940 // Include only those elements still alive
14941 Vector<FiniteElement*>::iterator it = std::find(
14942 deleted_elements.begin(), deleted_elements.end(), tmp_ele_pt);
14943 // Only copy thoes elements not found on the deleted elements
14944 // container
14945 if (it == deleted_elements.end())
14946 {
14947 FiniteElement* add_ele_pt = backed_up_boundary_element_pt[e];
14948 this->Boundary_element_pt[b].push_back(add_ele_pt);
14949 const int face_index = backed_up_face_index_at_boundary[e];
14950 this->Face_index_at_boundary[b].push_back(face_index);
14951 } // if (tmp_ele_pt != 0)
14952
14953 } // for (n < nold_bnd_ele)
14954
14955 // ... continue with the boundary elements in specific regions
14956
14957 // Loop over the regions
14958 for (unsigned ir = 0; ir < n_regions; ir++)
14959 {
14960 // Get the region id
14961 const unsigned region_id =
14962 static_cast<unsigned>(this->region_attribute(ir));
14963
14964 // Get the old number of boundary elements in region
14965 const unsigned nold_bnd_region_ele =
14966 ntmp_boundary_elements_in_region[b][ir];
14967
14968 // Loop over the boundary region elements and check those still
14969 // alive
14970 for (unsigned e = 0; e < nold_bnd_region_ele; e++)
14971 {
14972 // Get the element
14973 FiniteElement* tmp_ele_pt =
14974 backed_up_boundary_region_element_pt[ir][e];
14975 // Include only those elements still alive
14976 Vector<FiniteElement*>::iterator it = std::find(
14977 deleted_elements.begin(), deleted_elements.end(), tmp_ele_pt);
14978 // Only copy those elements not found on the deleted elements
14979 // container
14980 if (it == deleted_elements.end())
14981 {
14982 FiniteElement* add_ele_pt =
14983 backed_up_boundary_region_element_pt[ir][e];
14984 this->Boundary_region_element_pt[b][region_id].push_back(
14985 add_ele_pt);
14986 const int face_index =
14987 backed_up_face_index_at_boundary_region[ir][e];
14988 this->Face_index_region_at_boundary[b][region_id].push_back(
14989 face_index);
14990 } // if (tmp_ele_pt != 0)
14991
14992 } // for (n < nbound_ele)
14993
14994 } // for (ir < n_regions)
14995
14996 // ----------------------------------------------------------------
14997 // Now copy all those elements created after the re-establishment
14998 // of halo and haloed elements
14999 // ----------------------------------------------------------------
15000 // Loop over the boundary elements
15001 for (unsigned e = nold_bnd_ele; e < nbound_ele; e++)
15002 {
15003 FiniteElement* add_ele_pt = backed_up_boundary_element_pt[e];
15004 this->Boundary_element_pt[b].push_back(add_ele_pt);
15005 const int face_index = backed_up_face_index_at_boundary[e];
15006 this->Face_index_at_boundary[b].push_back(face_index);
15007 } // for (e < nbound_ele)
15008
15009 // Now add the boundary elements in regions
15010
15011 // Loop over the regions
15012 for (unsigned ir = 0; ir < n_regions; ir++)
15013 {
15014 // Get the region id
15015 const unsigned region_id =
15016 static_cast<unsigned>(this->region_attribute(ir));
15017
15018 // Get the old number of boundary elements in region
15019 const unsigned nold_bnd_region_ele =
15020 ntmp_boundary_elements_in_region[b][ir];
15021
15022 // Get the new number of boundary elements in region
15023 const unsigned nbnd_region_ele =
15024 this->nboundary_element_in_region(b, region_id);
15025
15026 // Loop over the boundary region elements and check those still
15027 // alive
15028 for (unsigned e = nold_bnd_region_ele; e < nbnd_region_ele; e++)
15029 {
15030 FiniteElement* add_ele_pt =
15031 backed_up_boundary_region_element_pt[ir][e];
15032 this->Boundary_region_element_pt[b][region_id].push_back(add_ele_pt);
15033 const int face_index = backed_up_face_index_at_boundary_region[ir][e];
15034 this->Face_index_region_at_boundary[b][region_id].push_back(
15035 face_index);
15036 } // for (e < nbnd_region_ele)
15037
15038 } // for (ir < n_regions)
15039
15040 } // for (b < nbound)
15041
15042 // Lookup scheme has now been setup yet
15043 Lookup_for_elements_next_boundary_is_setup = true;
15044 }
15045
15046#endif // OOMPH_HAS_MPI
15047
15048#ifdef OOMPH_HAS_TRIANGLE_LIB
15049
15050 //========================================================================
15051 /// Build a new TriangulateIO object based on target areas specified
15052 //========================================================================
15053 template<class ELEMENT>
15055 TriangulateIO& triangulate_io,
15056 const Vector<double>& target_area,
15057 struct TriangulateIO& triangle_refine)
15058 {
15059 // Initialize
15061
15062 // Store the global number of vertices and segments
15063 // in the list
15064 unsigned n_points = triangulate_io.numberofpoints;
15065 triangle_refine.numberofpoints = n_points;
15066
15067 unsigned n_segments = triangulate_io.numberofsegments;
15068 triangle_refine.numberofsegments = n_segments;
15069
15070 // Initialization of the TriangulateIO objects to store the values
15071 triangle_refine.pointlist =
15072 (double*)malloc(triangulate_io.numberofpoints * 2 * sizeof(double));
15073 triangle_refine.pointmarkerlist =
15074 (int*)malloc(triangulate_io.numberofpoints * sizeof(int));
15075 triangle_refine.segmentlist =
15076 (int*)malloc(triangulate_io.numberofsegments * 2 * sizeof(int));
15077 triangle_refine.segmentmarkerlist =
15078 (int*)malloc(triangulate_io.numberofsegments * sizeof(int));
15079
15080 // Storing the point's coordinates in the list
15081 // and in two vectors with x and y coordinates
15082 Vector<double> x_coord(n_points);
15083 Vector<double> y_coord(n_points);
15084
15085 for (unsigned count_point = 0; count_point < n_points * 2; count_point++)
15086 {
15087 triangle_refine.pointlist[count_point] =
15088 triangulate_io.pointlist[count_point];
15089
15090 // Even vaules represent the x coordinate
15091 // Odd values represent the y coordinate
15092 if (count_point % 2 == 0)
15093 {
15094 x_coord[count_point / 2] = triangulate_io.pointlist[count_point];
15095 }
15096 else
15097 {
15098 y_coord[(count_point - 1) / 2] = triangulate_io.pointlist[count_point];
15099 }
15100 }
15101
15102 // Store the point's markers in the list
15103 for (unsigned count_marker = 0; count_marker < n_points; count_marker++)
15104 {
15105 triangle_refine.pointmarkerlist[count_marker] =
15106 triangulate_io.pointmarkerlist[count_marker];
15107 }
15108
15109 // Storing the segment's edges in the list
15110 for (unsigned count_seg = 0; count_seg < n_segments * 2; count_seg++)
15111 {
15112 triangle_refine.segmentlist[count_seg] =
15113 triangulate_io.segmentlist[count_seg];
15114 }
15115
15116 // Store the segment's markers in the list
15117 for (unsigned count_markers = 0; count_markers < n_segments;
15118 count_markers++)
15119 {
15120 triangle_refine.segmentmarkerlist[count_markers] =
15121 triangulate_io.segmentmarkerlist[count_markers];
15122 }
15123
15124 // Store the hole's center coordinates
15125 unsigned n_holes = triangulate_io.numberofholes;
15126 triangle_refine.numberofholes = n_holes;
15127
15128 triangle_refine.holelist =
15129 (double*)malloc(triangulate_io.numberofholes * 2 * sizeof(double));
15130
15131 // Loop over the holes to get centre coords
15132 for (unsigned count_hole = 0; count_hole < n_holes * 2; count_hole++)
15133 {
15134 triangle_refine.holelist[count_hole] =
15135 triangulate_io.holelist[count_hole];
15136 }
15137
15138 // Store the triangles values
15139 unsigned n_triangles = triangulate_io.numberoftriangles;
15140 triangle_refine.numberoftriangles = n_triangles;
15141
15142#ifdef PARANOID
15143 if (n_triangles != target_area.size())
15144 {
15145 std::stringstream err;
15146 err << "Number of triangles in triangulate_io=" << n_triangles
15147 << " doesn't match\n"
15148 << "size of target area vector (" << target_area.size() << ")\n";
15149 throw OomphLibError(
15150 err.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
15151 }
15152#endif
15153
15154 unsigned n_corners = triangulate_io.numberofcorners;
15155 triangle_refine.numberofcorners = n_corners;
15156
15157 triangle_refine.trianglelist =
15158 (int*)malloc(triangulate_io.numberoftriangles * 3 * sizeof(int));
15159
15160 // Store the triangle's corners in the list and get element sizes
15161 for (unsigned count_tri = 0; count_tri < n_triangles * 3; count_tri++)
15162 {
15163 triangle_refine.trianglelist[count_tri] =
15164 triangulate_io.trianglelist[count_tri];
15165 }
15166
15167 // Store the triangle's area in the list
15168 triangle_refine.trianglearealist =
15169 (double*)malloc(triangulate_io.numberoftriangles * sizeof(double));
15170 for (unsigned count_area = 0; count_area < n_triangles; count_area++)
15171 {
15172 triangle_refine.trianglearealist[count_area] = target_area[count_area];
15173 }
15174
15175 // Store the triangles attributes in the list
15176 triangle_refine.numberoftriangleattributes =
15177 triangulate_io.numberoftriangleattributes;
15178
15179 triangle_refine.triangleattributelist = (double*)malloc(
15180 triangulate_io.numberoftriangles *
15181 triangulate_io.numberoftriangleattributes * sizeof(double));
15182 for (unsigned count_attribute = 0;
15183 count_attribute <
15184 (n_triangles * triangulate_io.numberoftriangleattributes);
15185 count_attribute++)
15186 {
15187 triangle_refine.triangleattributelist[count_attribute] =
15188 triangulate_io.triangleattributelist[count_attribute];
15189 }
15190 }
15191
15192#ifdef OOMPH_HAS_MPI
15193
15194 // ===================================================================
15195 // The comparison class for the map that sorts the nodes on the
15196 // shared boundary (using a lexicographic order)
15197 // ===================================================================
15199 {
15200 // Tolerance for lower-left comparison
15201 static double Tol;
15202
15203
15204 // Comparison operator for "lower left" ordering
15205 bool operator()(const std::pair<double, double>& lhs,
15206 const std::pair<double, double>& rhs) const
15207 {
15208 double diff_y = lhs.second - rhs.second;
15209 if (diff_y < -Tol) // (lhs.second < rhs.second)
15210 {
15211 return true;
15212 }
15213 else
15214 {
15215 // Are they "equal" with 1.0e-14 tolerance?
15216 if (diff_y < Tol) // (lhs.second == rhs.second)
15217 {
15218#ifdef PARANOID
15219 double diff_x = lhs.first - rhs.first;
15220 if (fabs(diff_x) < Tol)
15221 {
15222 std::ostringstream warning_message;
15223 warning_message
15224 << "Dodgy \"lower left\" (lexicographic) comparison "
15225 << "of points with cooordinates: "
15226 << " lhs = ( " << lhs.first << " , " << lhs.second << " ) \n"
15227 << " rhs = ( " << rhs.first << " , " << rhs.second << " ) \n"
15228 << "x and y coordinates differ by less than tolerance!\n"
15229 << "diff_x = " << diff_x << "\n"
15230 << "diff_y = " << diff_y << "\n"
15231 << "Tol = " << Tol << "\n";
15232 OomphLibError(warning_message.str(),
15233 OOMPH_CURRENT_FUNCTION,
15234 OOMPH_EXCEPTION_LOCATION);
15235 }
15236#endif
15237 if (lhs.first < rhs.first)
15238 {
15239 return true;
15240 }
15241 else
15242 {
15243 return false;
15244 }
15245 }
15246 else
15247 {
15248 return false;
15249 }
15250 }
15251
15252
15253 // if (lhs.second < rhs.second)
15254 // {
15255 // return true;
15256 // }
15257 // else
15258 // {
15259 // // // Are "equal" with 1.0e-14 tolerance
15260 // // if (lhs.second - rhs.second < 1.0e-14)
15261 // // Are equal?
15262 // if (lhs.second == rhs.second)
15263 // {
15264 // if (lhs.first < rhs.first)
15265 // {
15266 // return true;
15267 // }
15268 // else
15269 // {
15270 // return false;
15271 // }
15272 // }
15273 // else
15274 // {
15275 // return false;
15276 // }
15277 // }
15278 }
15279
15280 } Bottom_left_sorter; // struct classcomp
15281
15282
15283 // Assign value for tolerance
15284 double classcomp::Tol = 1.0e-14;
15285
15286
15287 //======================================================================
15288 // Sort the nodes on shared boundaries so that the processors that share
15289 // a boundary agree with the order of the nodes on the boundary
15290 //======================================================================
15291 template<class ELEMENT>
15293 {
15294 // Get the shared boundaries in this processor
15295 Vector<unsigned> my_rank_shared_boundaries_ids;
15296 this->shared_boundaries_in_this_processor(my_rank_shared_boundaries_ids);
15297
15298 // Get the number of shared boundaries
15299 const unsigned nmy_rank_shd_bnd = my_rank_shared_boundaries_ids.size();
15300
15301 // Loop over the shared boundaries
15302 for (unsigned i = 0; i < nmy_rank_shd_bnd; i++)
15303 {
15304 // A map is used to sort the nodes using their coordinates as the key
15305 // of the map
15306 // std::map<std::pair<double, double>, Node*> sorted_nodes_pt;
15307 std::map<std::pair<double, double>, Node*, classcomp> sorted_nodes_pt;
15308
15309
15310#ifdef PARANOID
15311
15312 // Check min distance between nodes; had better be less than the
15313 // tolerance used for the bottom left sorting
15314 double min_distance_squared = DBL_MAX;
15315
15316#endif
15317
15318 // Get the boundary id
15319 const unsigned b = my_rank_shared_boundaries_ids[i];
15320
15321 // Get the number of nodes on the current boundary
15322 const unsigned nbnd_node = this->nshared_boundary_node(b);
15323
15324 // Go through all the nodes on the boundary and temporarily store
15325 // them on the map container
15326 for (unsigned i_node = 0; i_node < nbnd_node; i_node++)
15327 {
15328 Node* node_pt = this->shared_boundary_node_pt(b, i_node);
15329 std::pair<double, double> vertex =
15330 std::make_pair(node_pt->x(0), node_pt->x(1));
15331 sorted_nodes_pt[vertex] = node_pt;
15332
15333
15334#ifdef PARANOID
15335
15336 // Check for minimum distance
15337 for (unsigned j_node = 0; j_node < nbnd_node; j_node++)
15338 {
15339 if (i_node != j_node)
15340 {
15341 Node* node2_pt = this->shared_boundary_node_pt(b, j_node);
15342
15343 // Squared distance
15344 double squared_distance = 0.0;
15345 for (unsigned ii = 0; ii < 2; ii++)
15346 {
15347 squared_distance += (node_pt->x(ii) - node2_pt->x(ii)) *
15348 (node_pt->x(ii) - node2_pt->x(ii));
15349 }
15350 if (squared_distance < min_distance_squared)
15351 {
15352 min_distance_squared = squared_distance;
15353 }
15354 }
15355 }
15356
15357 if (sqrt(min_distance_squared) < Bottom_left_sorter.Tol)
15358 {
15359 std::ostringstream warning_message;
15360 warning_message << "Minimum distance between nodes on boundary " << b
15361 << "\n"
15362 << "is " << sqrt(min_distance_squared)
15363 << " which is less than "
15364 << "Bottom_left_sorter.Tol = "
15365 << Bottom_left_sorter.Tol << "\n"
15366 << "This may screw up the ordering of the nodes on "
15367 "shared boundaries\n";
15368 OomphLibWarning(warning_message.str(),
15369 OOMPH_CURRENT_FUNCTION,
15370 OOMPH_EXCEPTION_LOCATION);
15371 }
15372
15373#endif
15374 }
15375
15376 unsigned counter = 0;
15377 // Resize the sorted shared boundary node vector
15378 this->Sorted_shared_boundary_node_pt[b].resize(nbnd_node);
15379
15380 // Now go through the map container, get the elements and store their
15381 // members on the Sorted_shared_boundary_node_pt container
15382 // The map has already sorted the nodes, now they keep the same sorting
15383 // on all processors
15384 for (std::map<std::pair<double, double>, Node*>::iterator it_map =
15385 sorted_nodes_pt.begin();
15386 it_map != sorted_nodes_pt.end();
15387 it_map++)
15388 {
15389 // Store the pointer to the node
15390 this->Sorted_shared_boundary_node_pt[b][counter++] = (*it_map).second;
15391 }
15392
15393 } // for (i < nmy_rank_shd_bnd)
15394 }
15395
15396 //========================================================================
15397 // Re-establish the shared boundary elements after the adaptation
15398 // process (the updating of shared nodes is optional and performed by
15399 // default)
15400 //========================================================================
15401 template<class ELEMENT>
15403 reset_shared_boundary_elements_and_nodes(const bool flush_elements,
15404 const bool update_elements,
15405 const bool flush_nodes,
15406 const bool update_nodes)
15407 {
15408 // Get the rank of the current processor
15409 const unsigned my_rank = this->communicator_pt()->my_rank();
15410
15411 // Go through the boundaries know as shared boundaries and copy the
15412 // elements to the corresponding storage
15413
15414 // Get the initial shared boundary id
15415 const unsigned initial_id = this->initial_shared_boundary_id();
15416
15417 // Get the final shared boundary id
15418 const unsigned final_id = this->final_shared_boundary_id();
15419
15420 if (flush_elements)
15421 {
15422 // Flush the shared boundaries storage for elements
15423 this->flush_shared_boundary_element();
15424 // .. and also flush the face indexes associated with the element
15425 this->flush_face_index_at_shared_boundary();
15426 } // if (flush_elements)
15427
15428 if (flush_nodes)
15429 {
15430 // Flush the shared boundaries storage for nodes
15431 this->flush_shared_boundary_node();
15432 } // if (flush_nodes)
15433
15434 for (unsigned b = initial_id; b < final_id; b++)
15435 {
15436 // Check if the boundary is on the current processor
15437 Vector<unsigned> procs_from_shrd_bnd;
15438 procs_from_shrd_bnd = this->shared_boundary_from_processors(b);
15439 bool current_processor_has_b_boundary = false;
15440 const unsigned n_procs_from_shrd_bnd = procs_from_shrd_bnd.size();
15441 for (unsigned p = 0; p < n_procs_from_shrd_bnd; p++)
15442 {
15443 if (procs_from_shrd_bnd[p] == my_rank)
15444 {
15445 current_processor_has_b_boundary = true;
15446 break; // break for (p < n_procs_from_shrd_bnd)
15447 }
15448 } // for (p < n_procs_from_shrd_bnd)
15449
15450 if (current_processor_has_b_boundary)
15451 {
15452 if (update_elements)
15453 {
15454 const unsigned nboundary_ele = this->nboundary_element(b);
15455 for (unsigned e = 0; e < nboundary_ele; e++)
15456 {
15457 // Get the boundary element and add it to the shared
15458 // boundary elements structure
15459 FiniteElement* bnd_ele_pt = this->boundary_element_pt(b, e);
15460 this->add_shared_boundary_element(b, bnd_ele_pt);
15461 // ... do the same with the face index information
15462 int face_index = this->face_index_at_boundary(b, e);
15463 this->add_face_index_at_shared_boundary(b, face_index);
15464 } // for (e < nboundary_element)
15465 } // if (update_elements)
15466
15467 if (update_nodes)
15468 {
15469 const unsigned nboundary_node = this->nboundary_node(b);
15470 for (unsigned n = 0; n < nboundary_node; n++)
15471 {
15472 Node* bnd_node_pt = this->boundary_node_pt(b, n);
15473 this->add_shared_boundary_node(b, bnd_node_pt);
15474 } // for (n < nboundary_node)
15475 } // if (update_nodes)
15476
15477 } // if (current_processor_has_b_boundary)
15478 } // for (b < final_id)
15479 }
15480
15481 //======================================================================
15482 // Sort the nodes on shared boundaries so that the processors that share
15483 // a boundary agree with the order of the nodes on the boundary
15484 //======================================================================
15485 template<class ELEMENT>
15487 {
15488 // Get the number of processors
15489 unsigned nproc = this->communicator_pt()->nproc();
15490 // Get the rank of the current processor
15491 unsigned my_rank = this->communicator_pt()->my_rank();
15492
15493 // Get some timings
15494 double tt_start = 0.0;
15495 double tt_end = 0.0;
15497 {
15498 tt_start = TimingHelpers::timer();
15499 }
15500
15501 // -------------------------------------------------------------------
15502 // BEGIN: Get the node names and the shared nodes
15503 // -------------------------------------------------------------------
15504
15505 // Container where to store the nodes on shared boundaries no
15506 // associated with the processor that receives the elements/nodes
15507 // other_proc_shd_bnd_node_pt[iproc][jproc][shd_bnd_id][index]
15509 other_proc_shd_bnd_node_pt(nproc);
15510 // Resize the container
15511 for (unsigned iproc = 0; iproc < nproc; iproc++)
15512 {
15513 // Resize the container
15514 other_proc_shd_bnd_node_pt[iproc].resize(nproc);
15515 for (unsigned jproc = 0; jproc < nproc; jproc++)
15516 {
15517 // Get the number of shared boundaries
15518 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
15519 const unsigned final_shd_bnd_id = this->final_shared_boundary_id();
15520 const unsigned nshared_bound = final_shd_bnd_id - initial_shd_bnd_id;
15521 other_proc_shd_bnd_node_pt[iproc][jproc].resize(nshared_bound);
15522 } // for (jproc < nproc)
15523
15524 } // for (iproc < nproc)
15525
15526 // Store the global node names
15527 // global_node_name[x][ ][ ] Global node number
15528 // global_node_name[ ][x][ ] Global node names
15529 // global_node_name[ ][ ][x] Global node info.
15530 Vector<Vector<Vector<unsigned>>> global_node_names;
15531
15532 // Creates a map between the node name and the index of the global
15533 // node so we can access all its node names
15534 std::map<Vector<unsigned>, unsigned> node_name_to_global_index;
15535
15536 // Store the global shared nodes pointers
15537 Vector<Node*> global_shared_node_pt;
15538
15539 // Get the time for computation of global nodes names and shared
15540 // nodes
15541 double t_start_global_node_names_and_shared_nodes = TimingHelpers::timer();
15542
15543 // Compute all the names of the nodes and fill in the
15544 // "other_proc_shd_bnd_node_pt" structure with the nodes that live
15545 // on this processor (my_rank) by looking over all their names
15546 compute_global_node_names_and_shared_nodes(other_proc_shd_bnd_node_pt,
15547 global_node_names,
15548 node_name_to_global_index,
15549 global_shared_node_pt);
15550
15551 // Compute the number of elements before adding new ones
15552 const unsigned n_ele = this->nelement();
15553
15554 if (Print_timings_level_adaptation > 1)
15555 {
15556 // The total time for computation of global nodes names and
15557 // shared nodes
15558 double t_final_global_node_names_and_shared_nodes =
15559 TimingHelpers::timer() - t_start_global_node_names_and_shared_nodes;
15560 oomph_info << "CPU for computing global node names and shared nodes "
15561 << "[n_ele=" << n_ele
15562 << "]: " << t_final_global_node_names_and_shared_nodes
15563 << std::endl;
15564 }
15565
15566 // -------------------------------------------------------------------
15567 // END: Get the node names and the shared nodes
15568 // -------------------------------------------------------------------
15569
15570 // -------------------------------------------------------------------
15571 // BEGIN: Using the global node names each processor sends info. of
15572 // the nodes shared with other processors regarding whether they are
15573 // on an original boundary or not. This is required so that at the
15574 // re-generation of halo(ed) elements stage they have the updated
15575 // information
15576 // -------------------------------------------------------------------
15577
15578 // Get the time for sending info. of shared nodes on original
15579 // boundaries
15580 double t_start_send_info_shd_nodes_on_original_bnds =
15582
15583 // Send the boundary node info. of nodes on shared boundaries across
15584 // processors
15585 send_boundary_node_info_of_shared_nodes(
15586 global_node_names, node_name_to_global_index, global_shared_node_pt);
15587
15588 if (Print_timings_level_adaptation > 1)
15589 {
15590 // The total time for sending info. of shared nodes lying on
15591 // original boundaries
15593 << "CPU for sending info. of shared nodes on original boundaries: "
15594 << TimingHelpers::timer() - t_start_send_info_shd_nodes_on_original_bnds
15595 << std::endl;
15596 }
15597
15598 // -------------------------------------------------------------------
15599 // END: Using the global node names each processor sends info. of
15600 // the nodes shared with other processors regarding whether they are
15601 // on an original boundary or not. This is required so that at the
15602 // re-generation of halo(ed) elements stage they have the updated
15603 // information
15604 // -------------------------------------------------------------------
15605
15606 // -------------------------------------------------------------------
15607 // BEGIN: Identify the elements of the mesh that have nodes on the
15608 // shared boundaries
15609 // -------------------------------------------------------------------
15610
15611 // Store the elements that have a node on a shared boundary with
15612 // other processors
15613 // ele_with_node_on_shd_bnd_pt[x][ ][ ]: iproc
15614 // ele_with_node_on_shd_bnd_pt[ ][x][ ]: ishd boundary with iproc
15615 // ele_with_node_on_shd_bnd_pt[ ][ ][x]: element with node on shared
15616 // boundary with iproc
15617 Vector<Vector<Vector<FiniteElement*>>> ele_with_node_on_shd_bnd_pt(nproc);
15618 // Resize the container with the number of shared boundaries within
15619 // each processor
15620
15621 // loop over the processors
15622 for (unsigned iproc = 0; iproc < nproc; iproc++)
15623 {
15624 const unsigned n_shd_bnd_iproc = this->nshared_boundaries(my_rank, iproc);
15625 ele_with_node_on_shd_bnd_pt[iproc].resize(n_shd_bnd_iproc);
15626 } // for (iproc < nproc)
15627
15628 // Go through all the elements and check whether any of their nodes
15629 // lies on any of the shared boundaries
15630
15631 // loop over the elements
15632 for (unsigned e = 0; e < n_ele; e++)
15633 {
15634 // Get the element
15635 FiniteElement* ele_pt = this->finite_element_pt(e);
15636 // Get the number of nodes
15637 const unsigned n_nodes = ele_pt->nnode();
15638 // loop over the nodes and check whether any of them lies on a
15639 // shared boundary
15640 for (unsigned n = 0; n < n_nodes; n++)
15641 {
15642 // Get the node
15643 Node* node_pt = ele_pt->node_pt(n);
15644
15645 // Now check whether the current node lies on a shared boundary
15646 // within any other processor
15647
15648 // loop over the processors
15649 for (unsigned iproc = 0; iproc < nproc; iproc++)
15650 {
15651 // The number of boundaries shared with the current processor
15652 // (if iproc==my_rank then there are no shared boundaries
15653 // between them)
15654 const unsigned n_shd_bnd_iproc =
15655 this->nshared_boundaries(my_rank, iproc);
15656
15657 // There are no info. with myself
15658 if (iproc != my_rank && n_shd_bnd_iproc > 0)
15659 {
15660 // Get the boundaries ids of the shared boundaries with
15661 // iproc processor
15662 Vector<unsigned> shd_bnd_ids =
15663 this->shared_boundaries_ids(my_rank, iproc);
15664
15665 // Loop over shd bnds with processor "iproc"
15666 for (unsigned isb = 0; isb < n_shd_bnd_iproc; isb++)
15667 {
15668 const unsigned shd_bnd_id = shd_bnd_ids[isb];
15669 const unsigned n_ele_shd_bnd =
15670 this->nshared_boundary_element(shd_bnd_id);
15671
15672 // Check if the node is on this boundary only if there are
15673 // elements on it
15674 if (n_ele_shd_bnd > 0 &&
15675 this->is_node_on_shared_boundary(shd_bnd_id, node_pt))
15676 {
15677 // Add the element into those that have a
15678 // node on the current shared boundary
15679 ele_with_node_on_shd_bnd_pt[iproc][isb].push_back(ele_pt);
15680
15681 } // Are there elements on the boundary and the node lies
15682 // on this boundary
15683
15684 } // for (isb < n_shd_bnd_iproc)
15685
15686 } // if (iproc != my_rank && n_shd_bnd_iproc > 0)
15687
15688 } // for (iproc < nproc)
15689
15690 } // for (n < n_nodes)
15691
15692 } // for (e < n_ele)
15693
15694 // -------------------------------------------------------------------
15695 // END: Identify the elements of the mesh that have nodes on the
15696 // shared boundaries
15697 // -------------------------------------------------------------------
15698
15699 // -------------------------------------------------------------------
15700 // BEGIN: Create the halo(ed) elements. Loop over the processors and
15701 // the shared boundaries within each processor. Get the elements on
15702 // the shared boundaries, mark them as haloed in this processor and
15703 // as halo on the element that will receive the info.
15704 // -------------------------------------------------------------------
15705
15706 // ********************************************************************
15707 // General strategy:
15708 // 1) Go through all the elements on the shared boundaries, mark these
15709 // elements as haloed, same as their nodes.
15710 // 2) Package the info. of the nodes and the elements.
15711 // 3) Send and receive the info across processors
15712 // 4) Unpackage it and create halo elements and nodes as indicated by
15713 // the received info.
15714 // ********************************************************************
15715
15716 // Keep track of the currently created nodes within each
15717 // processor. We need to keep track of these nodes so they can be
15718 // referred at a second stage.
15719 Vector<Vector<Node*>> iproc_currently_created_nodes_pt(nproc);
15720
15721 // Get the time to re-generate halo(ed) elements/nodes (first stage)
15722 double t_start_regenerate_halo_ed_elements_nodes_first_stage =
15724
15725 // Go through all processors
15726 for (unsigned iproc = 0; iproc < nproc; iproc++)
15727 {
15728 // Send and receive info. to/from other processors
15729 if (iproc != my_rank)
15730 {
15731 // Get the number of boundaries shared with the send proc (iproc)
15732 const unsigned nshared_boundaries_with_iproc =
15733 this->nshared_boundaries(my_rank, iproc);
15734
15735 if (nshared_boundaries_with_iproc > 0)
15736 {
15737 // ******************************************************************
15738 // Stage 1
15739 // ******************************************************************
15740 // Step (1) Mark the elements adjacent to the shared boundaries as
15741 // haloed, mark the nodes on these elements as haloed nodes
15742 // Step (2) Create packages of information indicating the generation
15743 // of halo elements and nodes
15744 // ******************************************************************
15745
15746 // Clean send and receive buffers
15747 Flat_packed_unsigneds.clear();
15748 Flat_packed_doubles.clear();
15749#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15751#endif
15752
15753 // Get the boundaries ids shared with "iproc"
15754 Vector<unsigned> bound_shared_with_iproc;
15755 bound_shared_with_iproc = this->shared_boundaries_ids(my_rank, iproc);
15756
15757 // Loop over shared boundaries with processor "iproc"
15758 for (unsigned bs = 0; bs < nshared_boundaries_with_iproc; bs++)
15759 {
15760 const unsigned bnd_id = bound_shared_with_iproc[bs];
15761 // DEBP(bnd_id);
15762 const unsigned nel_bnd = this->nshared_boundary_element(bnd_id);
15763 // DEBP(nel_bnd);
15764
15765 // Container to store the elements marked as haloed
15766 Vector<FiniteElement*> haloed_element;
15767
15768 // All the elements adjacent to the boundary should be
15769 // marked as haloed elements
15770 if (nel_bnd > 0)
15771 {
15772 // Map to know which element have been already added
15773 std::map<FiniteElement*, bool> already_added;
15774
15775 // Loop over the elements adjacent to boundary "bnd_id"
15776 for (unsigned e = 0; e < nel_bnd; e++)
15777 {
15778 // Get pointer to the element adjacent to boundary bnd_id
15779 FiniteElement* ele_pt =
15780 this->shared_boundary_element_pt(bnd_id, e);
15781
15782 // Check if the element has been already added. Elemets
15783 // are repeated if they have two faces on the shared
15784 // boundary
15785 if (!already_added[ele_pt])
15786 {
15787 // Add the element to the container of haloed elements
15788 haloed_element.push_back(ele_pt);
15789 // Mark the element as already added
15790 already_added[ele_pt] = true;
15791 }
15792
15793 } // for (e < nel_bnd)
15794
15795 // In addition to the elements on the boundary we also
15796 // need to mark (as haloed) any element on the mesh with a
15797 // node on the shared boundary
15798
15799 // Get the number of elements with a node on the current
15800 // shared boundary
15801 const unsigned n_ele_with_node_on_shd_bnd =
15802 ele_with_node_on_shd_bnd_pt[iproc][bs].size();
15803 // loop and add the elements that have a node on the
15804 // current shared boundary with the current processor
15805 for (unsigned iele = 0; iele < n_ele_with_node_on_shd_bnd; iele++)
15806 {
15807 // Get the element
15808 FiniteElement* ele_pt =
15809 ele_with_node_on_shd_bnd_pt[iproc][bs][iele];
15810 // Check if it has not been already added
15811 if (!already_added[ele_pt])
15812 {
15813 // Add it!!
15814 haloed_element.push_back(ele_pt);
15815 // Mark it as done
15816 already_added[ele_pt] = true;
15817 } // if (!already_added[ele_pt])
15818
15819 } // for (iele < n_ele_with_node_on_shd_bnd)
15820
15821 } // if (nel_bnd > 0)
15822
15823 // Get the total number of haloed elements
15824 const unsigned nhaloed_ele = haloed_element.size();
15825 // DEBP(nhaloed_ele);
15826 // DEBP(my_rank);
15827 // DEBP(iproc);
15828 // The very first data of the flat packed is the number of haloed
15829 // elements, this will be the number of halo element to create on
15830 // the receiver processor
15831 Flat_packed_unsigneds.push_back(nhaloed_ele);
15832#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15833 std::stringstream junk;
15834 junk << "Number of haloed elements " << nhaloed_ele;
15835 Flat_packed_unsigneds_string.push_back(junk.str());
15836#endif
15837
15838 // Loop over the marked haloed elements
15839 for (unsigned e = 0; e < nhaloed_ele; e++)
15840 {
15841 // Get pointer to the marked haloed element
15842 FiniteElement* ele_pt = haloed_element[e];
15843 const unsigned nroot_haloed_ele =
15844 this->nroot_haloed_element(iproc);
15845
15846 // Check if the element has been already added to the
15847 // halo(ed) scheme
15848 GeneralisedElement* gen_ele_pt = ele_pt;
15849 const unsigned haloed_ele_index =
15850 this->try_to_add_root_haloed_element_pt(iproc, gen_ele_pt);
15851
15852 // Was the element added or only returned the index of the
15853 // element
15854 if (nroot_haloed_ele == haloed_ele_index)
15855 {
15856 Flat_packed_unsigneds.push_back(1);
15857#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15859 "Haloed element needs to be constructed");
15860#endif
15861
15862 // Get additional info. related with the haloed element
15863 get_required_elemental_information_helper(iproc, ele_pt);
15864
15865 // Get the nodes on the element
15866 const unsigned nnodes = ele_pt->nnode();
15867 for (unsigned j = 0; j < nnodes; j++)
15868 {
15869 Node* node_pt = ele_pt->node_pt(j);
15870
15871 // Package the info. of the nodes
15872 // The destination processor goes in the arguments
15873 add_haloed_node_helper(iproc, node_pt);
15874
15875 } // for (j < nnodes)
15876 } // add the element and send its nodes
15877 else // The haloed element already exists
15878 {
15879 Flat_packed_unsigneds.push_back(0);
15880#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15882 "Haloed element already exists");
15883#endif
15884 Flat_packed_unsigneds.push_back(haloed_ele_index);
15885#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15887 "Index of existing haloed element");
15888#endif
15889 } // else (next_haloed_ele == external_haloed_ele_index)
15890 } // for (e < nel_bnd)
15891
15892 } // for (bs < nshared_boundaries_with_iproc)
15893
15894 // *******************************************************************
15895 // Stage (2)
15896 // *******************************************************************
15897 // Step (1) Send and receive the data to create halo elements and
15898 // nodes
15899 // *******************************************************************
15900 // The processor to which send the elements
15901 int send_proc = static_cast<int>(iproc);
15902 // The processor from which receive the elements
15903 int recv_proc = static_cast<int>(iproc);
15904 send_and_receive_elements_nodes_info(send_proc, recv_proc);
15905
15906 // *******************************************************************
15907 // Stage (3)
15908 // *******************************************************************
15909 // Step (1) Unpackage the info and create the halo elements and nodes
15910 // *******************************************************************
15911
15912 // Reset the counters
15915
15916 // Loop over shared boundaries with processor "iproc"
15917 for (unsigned bs = 0; bs < nshared_boundaries_with_iproc; bs++)
15918 {
15919 // Get the number of halo element to be created
15920 const unsigned nhaloed_ele =
15922
15923#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15926 << " Number of elements need to be constructed "
15928 << std::endl;
15929#endif
15930
15931 // Loop over boundaries shared with processor "urecv_proc"
15932 for (unsigned e = 0; e < nhaloed_ele; e++)
15933 {
15934 // Create halo element from received info. of "iproc"
15935 // processor on the current processor
15936 create_halo_element(iproc,
15937 iproc_currently_created_nodes_pt[iproc],
15938 other_proc_shd_bnd_node_pt,
15939 global_node_names,
15940 node_name_to_global_index,
15941 global_shared_node_pt);
15942
15943 } // for (e < nhaloed_ele)
15944
15945 } // for (bs < nshared_boundaries_with_iproc)
15946
15947 } // if (nshared_bound_recv_proc > 0)
15948
15949 } // if (iproc != my_rank)
15950
15951 } // for (iproc < nproc) (general loop to send and receive info.)
15952
15953 if (Print_timings_level_adaptation > 1)
15954 {
15955 // Get the time to re-generate halo(ed) elements/nodes (first stage)
15956 double t_final_regenerate_halo_ed_elements_nodes_first_stage =
15958 t_start_regenerate_halo_ed_elements_nodes_first_stage;
15959
15960 oomph_info << "CPU for re-generating halo(ed) elements/nodes "
15961 << "(first stage) [n_ele=" << n_ele << "]: "
15962 << t_final_regenerate_halo_ed_elements_nodes_first_stage
15963 << std::endl;
15964 }
15965
15966 // -------------------------------------------------------------------
15967 // END: Create the halo(ed) elements. Loop over the processors and
15968 // the shared boundaries within each processor. Get the elements on
15969 // the shared boundaries, mark them as haloed in this processor and
15970 // as halo on the element that will receive the info.
15971 // -------------------------------------------------------------------
15972
15973 // -------------------------------------------------------------------
15974 // BEGIN: Create any additional haloed element, those that dont lie
15975 // on a shared boundary but that shared a node with other processor
15976 // -------------------------------------------------------------------
15977
15978 // Get the time to re-generate halo(ed) elements/nodes (second stage)
15979 double t_start_regenerate_halo_ed_elements_nodes_second_stage =
15981
15982 // Create any additional halo(ed) elements between processors that
15983 // have no shared boundaries but that have shared nodes
15984 reset_halo_haloed_scheme_helper(other_proc_shd_bnd_node_pt,
15985 iproc_currently_created_nodes_pt,
15986 global_node_names,
15987 node_name_to_global_index,
15988 global_shared_node_pt);
15989
15990 if (Print_timings_level_adaptation > 1)
15991 {
15992 // Get the time to re-generate halo(ed) elements/nodes (second stage)
15993 double t_final_regenerate_halo_ed_elements_nodes_second_stage =
15995 t_start_regenerate_halo_ed_elements_nodes_second_stage;
15996
15997 oomph_info << "CPU for re-generating halo(ed) elements/nodes "
15998 << "(second stage) [n_ele=" << n_ele << "]: "
15999 << t_final_regenerate_halo_ed_elements_nodes_second_stage
16000 << std::endl;
16001 }
16002
16003 // -------------------------------------------------------------------
16004 // END: Create any additional haloed element, those that dont lie on
16005 // a shared boundary but that shared a node with other processor
16006 // -------------------------------------------------------------------
16007
16008 // Clean send and receive buffers
16009 Flat_packed_unsigneds.clear();
16010 Flat_packed_doubles.clear();
16011#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
16013#endif
16014
16015 // Document the timings for reseting halo and haloed scheme (without
16016 // classification of halo and haloed nodes)
16017 if (Print_timings_level_adaptation > 1)
16018 {
16019 tt_end = TimingHelpers::timer();
16020 oomph_info << "CPU for resetting halo-haloed scheme (without "
16021 "classification of halo and haloed nodes): "
16022 << tt_end - tt_start << std::endl;
16023 }
16024
16025 // ------------------------------------------------------------------
16026 // BEGIN: Classify halo(ed) elements and nodes
16027 // ------------------------------------------------------------------
16028 const bool report_stats = true;
16029 DocInfo tmp_doc_info;
16030 tmp_doc_info.disable_doc();
16031
16032 // Classify nodes
16033 this->classify_halo_and_haloed_nodes(tmp_doc_info, report_stats);
16034
16035 // Document the timings for reseting halo and haloed scheme (with
16036 // classification of halo and haloed nodes)
16037 if (Print_timings_level_adaptation > 1)
16038 {
16039 tt_end = TimingHelpers::timer();
16040 oomph_info << "CPU for resetting halo-haloed scheme (with classification "
16041 "of halo and haloed nodes): "
16042 << tt_end - tt_start << std::endl;
16043 }
16044
16045 // ------------------------------------------------------------------
16046 // END: Classify halo(ed) elements and nodes
16047 // ------------------------------------------------------------------
16048 }
16049
16050 //======================================================================
16051 // Compute the alias of the nodes on shared boundaries in this
16052 // (my_rank) processor with other processors. Also compute the alias
16053 // of nodes on shared boundaries of other processors with other
16054 // processors (useful when there is an element that requires to be
16055 // sent to this (my_rank) processor because there is a shared node
16056 // between this (my_rank) and other processors BUT there is not a
16057 // shared boundary between this and the other processor
16058 // ======================================================================
16059 template<class ELEMENT>
16062 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
16063 other_proc_shd_bnd_node_pt,
16064 Vector<Vector<Vector<unsigned>>>& global_node_names,
16065 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
16066 Vector<Node*>& global_shared_node_pt)
16067 {
16068 // Get the number of processors
16069 const unsigned nproc = this->communicator_pt()->nproc();
16070 // Get the rank of the current processor
16071 const unsigned my_rank = this->communicator_pt()->my_rank();
16072 // Get the communicator of the mesh
16073 OomphCommunicator* comm_pt = this->communicator_pt();
16074
16075 // ---------------------------------------------------------------
16076 // BEGIN: Get the elements adjacent to shared boundaries and give
16077 // a unique node number to the nodes on the shared boundaries in
16078 // this processor
16079 // ---------------------------------------------------------------
16080
16081 // Counter for the nodes on shared boundaries in this (my_rank)
16082 // processor
16083 unsigned counter_nodes = 0;
16084 // Keep track of visited nodes
16085 std::map<Node*, bool> done_node;
16086 // ... and its local node number
16087 std::map<Node*, unsigned> local_node_number;
16088 // ... and the inverted relation from local node number to node_pt
16089 Vector<Node*> local_node_pt;
16090
16091 // Stores the j-th node name associated with the i-th local node
16092 // on shared boundaries in this processor (my_rank)
16093 // local_node_names[i][j][0] = my_rank (this processor)
16094 // local_node_names[i][j][1] = iproc (the processor with which there
16095 // is a shared boundary)
16096 // local_node_names[i][j][2] = the shared boundary id between this
16097 // (my_rank) processor and iproc
16098 // processor
16099 // local_node_names[i][j][3] = the node index on the shared boundary
16100 // local_node_names[i][j][4] = the local node index (i). This may
16101 // be unnecessary since we alread know the
16102 // index but we also send this info. to
16103 // the root processor that is why we store
16104 // them here
16105 Vector<Vector<Vector<unsigned>>> local_node_names;
16106
16107 // loop over the processors
16108 for (unsigned iproc = 0; iproc < nproc; iproc++)
16109 {
16110 // There are not shared boundaries with myself
16111 if (iproc != my_rank)
16112 {
16113 // Get the number of shared boundaries with iproc
16114 const unsigned n_shd_bnds_with_iproc =
16115 this->nshared_boundaries(my_rank, iproc);
16116
16117 // Get the boundaries ids shared with iproc
16118 Vector<unsigned> bnd_shd_with_iproc =
16119 this->shared_boundaries_ids(my_rank, iproc);
16120
16121 // Loop over the shared boundaries with processor iproc
16122 for (unsigned ishd = 0; ishd < n_shd_bnds_with_iproc; ishd++)
16123 {
16124 // Keep track of visited nodes with this shared boundary
16125 std::map<Node*, bool> done_node_shd_bnd;
16126 // The boundary id
16127 unsigned shd_bnd_id = bnd_shd_with_iproc[ishd];
16128 // Get the number of element on the shared boundary
16129 const unsigned n_shd_bnd_ele =
16130 this->nshared_boundary_element(shd_bnd_id);
16131
16132 // loop over the elements adjacent to the shared boundary
16133 for (unsigned e = 0; e < n_shd_bnd_ele; e++)
16134 {
16135 // Get the element
16136 FiniteElement* ele_pt =
16137 this->shared_boundary_element_pt(shd_bnd_id, e);
16138
16139 // Get the number of nodes on the element
16140 const unsigned n_nodes = ele_pt->nnode();
16141
16142 // loop over the nodes of the current element
16143 for (unsigned n = 0; n < n_nodes; n++)
16144 {
16145 // Get the node
16146 Node* node_pt = ele_pt->node_pt(n);
16147
16148 // Has the node been visited with this shared boundary?
16149 // And, is this a node on the shd_bnd_id shared boundary
16150 // with processor iproc?
16151 if (!done_node_shd_bnd[node_pt] &&
16152 this->is_node_on_shared_boundary(shd_bnd_id, node_pt))
16153 {
16154 // Mark the done as done with this shared boundary
16155 done_node_shd_bnd[node_pt] = true;
16156
16157 // Get the index of the node on the shared boundary
16158 // -------------------------------------------------
16159 // Get the number of nodes on the shared boundary
16160 const unsigned n_nodes_shd_bnd =
16161 nsorted_shared_boundary_node(shd_bnd_id);
16162
16163 // The index
16164 unsigned index = 0;
16165
16166#ifdef PARANOID
16167 // Flag to know if the node has been found
16168 bool found_node_on_shared_boundary = false;
16169#endif
16170 // Loop over the nodes on the shared boundary to find
16171 // the node
16172 for (unsigned k = 0; k < n_nodes_shd_bnd; k++)
16173 {
16174 // Get the k-th node on the shared boundary
16175 Node* shd_bnd_node_pt =
16176 sorted_shared_boundary_node_pt(shd_bnd_id, k);
16177
16178 // Is the same node?
16179 if (shd_bnd_node_pt == node_pt)
16180 {
16181 // This is the index
16182 index = k;
16183#ifdef PARANOID
16184 // Mark as found
16185 found_node_on_shared_boundary = true;
16186#endif
16187 break; // break
16188
16189 } // if (shd_bnd_node_pt == node_pt)
16190
16191 } // for (k < n_nodes_shd_bnd)
16192
16193#ifdef PARANOID
16194 if (!found_node_on_shared_boundary)
16195 {
16196 std::ostringstream error_message;
16197 error_message << "The index of the node on boundary ("
16198 << shd_bnd_id << ") was not found.\n"
16199 << "These are the node coordinates\n"
16200 << "(" << node_pt->x(0) << "," << node_pt->x(1)
16201 << ").\n";
16202 throw OomphLibError(error_message.str(),
16203 OOMPH_CURRENT_FUNCTION,
16204 OOMPH_EXCEPTION_LOCATION);
16205 }
16206#endif
16207
16208 // Create the node name
16209 Vector<unsigned> node_name(5);
16210 node_name[0] = my_rank;
16211 node_name[1] = iproc;
16212 node_name[2] = shd_bnd_id;
16213 node_name[3] = index;
16214 // The node number is filled in the following if/else
16215 // node_name[4] = ?;
16216
16217 // Has the node already been visited?
16218 if (!done_node[node_pt])
16219 {
16220 // If not ...
16221
16222 // Add the node to the local nodes
16223 local_node_pt.push_back(node_pt);
16224
16225 // Assign a local node number to the node
16226 local_node_number[node_pt] = counter_nodes;
16227 // Store the local node number
16228 node_name[4] = counter_nodes;
16229 // Increase the counter of nodes
16230 counter_nodes++;
16231 // ... and mark it as visited
16232 done_node[node_pt] = true;
16233
16234 // Push back the node name (the first
16235 // one found for this node)
16236 Vector<Vector<unsigned>> first_node_name(1);
16237 first_node_name[0] = node_name;
16238 local_node_names.push_back(first_node_name);
16239 }
16240 else
16241 {
16242 // If yes ...
16243
16244 // Get the local node number
16245 unsigned node_number = local_node_number[node_pt];
16246
16247 // Store the local node number
16248 node_name[4] = node_number;
16249
16250 // Push back the node name for the
16251 // node number
16252 local_node_names[node_number].push_back(node_name);
16253 }
16254
16255 } // Is on shared boundary?
16256
16257 } // for (n < nnodes)
16258
16259 } // for (e < n_shd_bnd_ele)
16260
16261 } // for (ishd < n_shd_bnds_with_iproc)
16262
16263 } // if (iproc != my_rank)
16264
16265 } // for (iproc < nproc)
16266
16267 // ---------------------------------------------------------------
16268 // END: Get the elements adjacent to shared boundaries and give
16269 // a unique node number to the nodes on the shared boundaries in
16270 // this processor
16271 // ---------------------------------------------------------------
16272
16273 // ---------------------------------------------------------------
16274 // BEGIN: Package the names of the local nodes
16275 // ---------------------------------------------------------------
16276 // Counter for the number of names of the nodes
16277 unsigned n_total_local_names = 0;
16278 // Get the number of local nodes
16279 const unsigned n_local_nodes = local_node_names.size();
16280 // loop over the number of local nodes and get the number of names
16281 // of each node
16282 for (unsigned i = 0; i < n_local_nodes; i++)
16283 {
16284 // Get the number of names of the i-th local node
16285 const unsigned n_inode_names = local_node_names[i].size();
16286 // ... and add them to the total number of local names
16287 n_total_local_names += n_inode_names;
16288 } // for (i < n_local_nodes)
16289
16290 // We store five data per node name (my_rank,iproc,shd_bnd_id,idx,node#)
16291 // where node# is the node number on this processor (my_rank)
16292 const unsigned n_info_per_node_name = 5;
16293 // Storage for the flat package
16294 Vector<unsigned> flat_packed_send_udata(n_total_local_names *
16295 n_info_per_node_name);
16296 // A counter
16297 unsigned counter = 0;
16298 // loop over the local nodes
16299 for (unsigned i = 0; i < n_local_nodes; i++)
16300 {
16301 // Get the number of names of the i-th local node
16302 const unsigned n_inode_names = local_node_names[i].size();
16303 // loop over the names of the i-th local node
16304 for (unsigned j = 0; j < n_inode_names; j++)
16305 {
16306 // Store this processor id (my_rank)
16307 flat_packed_send_udata[counter++] = local_node_names[i][j][0];
16308 // Store the processor with which the shared boundary exist
16309 flat_packed_send_udata[counter++] = local_node_names[i][j][1];
16310 // Store the shared boundary id
16311 flat_packed_send_udata[counter++] = local_node_names[i][j][2];
16312 // Store the index of the node on the shared boundary
16313 flat_packed_send_udata[counter++] = local_node_names[i][j][3];
16314 // Store the local node number on this processor (my_rank)
16315 flat_packed_send_udata[counter++] = local_node_names[i][j][4];
16316 } // for (j < n_inode_names)
16317
16318 } // for (i < n_local_nodes)
16319
16320 // Reset the counter
16321 counter = 0;
16322
16323 // The number of data that will be sent to root from this
16324 // (my_rank) processor
16325 const unsigned n_udata_send_to_root = flat_packed_send_udata.size();
16326
16327 // ---------------------------------------------------------------
16328 // END: Package the names of the local nodes
16329 // ---------------------------------------------------------------
16330 // ---------------------------------------------------------------
16331 // BEGIN: Send the data to the root processor
16332 // ---------------------------------------------------------------
16333
16334 // The root processor is in charge of computing all the node names
16335 // of the nodes on the shared boundaries
16336
16337 // Choose the root processor
16338 const unsigned root_processor = 0;
16339
16340 // The vector where the root processor receives how many names
16341 // will receive from the other processors
16342 Vector<unsigned> root_n_names_per_processor(nproc);
16343
16344 // Send the number of names that the root processor will receive
16345 // from each processor
16346 MPI_Gather(&n_total_local_names,
16347 1,
16348 MPI_UNSIGNED,
16349 &root_n_names_per_processor[0],
16350 1,
16351 MPI_UNSIGNED,
16352 root_processor,
16353 comm_pt->mpi_comm());
16354
16355 // Get the total number of data to receive from all processor in
16356 // root
16357 unsigned root_n_total_udata_receive = 0;
16358 Vector<int> root_n_udata_to_receive(nproc, 0);
16359 for (unsigned iproc = 0; iproc < nproc; iproc++)
16360 {
16361 root_n_udata_to_receive[iproc] =
16362 root_n_names_per_processor[iproc] * n_info_per_node_name;
16363 root_n_total_udata_receive += root_n_udata_to_receive[iproc];
16364 }
16365
16366 // Stores and compute the offsets (in root) for the data received
16367 // from each processor
16368 Vector<int> root_uoffsets_receive(nproc, 0);
16369 root_uoffsets_receive[0] = 0;
16370 for (unsigned iproc = 1; iproc < nproc; iproc++)
16371 {
16372 // Compute the offset to obtain the data from each processor
16373 root_uoffsets_receive[iproc] =
16374 root_uoffsets_receive[iproc - 1] + root_n_udata_to_receive[iproc - 1];
16375 }
16376
16377 // Create at least one entry so we don't get a seg fault below
16378 if (flat_packed_send_udata.size() == 0)
16379 {
16380 flat_packed_send_udata.resize(1);
16381 }
16382
16383 // Vector where to receive the info on root from all processors
16384 Vector<unsigned> root_flat_packed_receive_udata(root_n_total_udata_receive);
16385 // Only root receive data, the others dont, then resize the
16386 // container to have at least one entry
16387 if (my_rank != root_processor)
16388 {
16389 // Create at least one entry so we don't get a seg fault below
16390 if (root_flat_packed_receive_udata.size() == 0)
16391 {
16392 root_flat_packed_receive_udata.resize(1);
16393 }
16394 } // if (my_rank!=root_processor)
16395
16396 // Send the info. to the root processor
16397 MPI_Gatherv(&flat_packed_send_udata[0], // Flat package to send
16398 // info. from each
16399 // processor
16400 n_udata_send_to_root, // Total number of data send
16401 // from each processor to root
16402 MPI_UNSIGNED,
16403 &root_flat_packed_receive_udata[0], // Container where
16404 // to receive the
16405 // info. from all
16406 // processors
16407 &root_n_udata_to_receive[0], // Number of data to
16408 // receive from each
16409 // processor
16410 &root_uoffsets_receive[0], // The offset to store the
16411 // info. from each
16412 // processor
16413 MPI_UNSIGNED,
16414 root_processor, // The processor that receives all the
16415 // info.
16416 comm_pt->mpi_comm());
16417
16418 // Clear and resize the flat package to send
16419 flat_packed_send_udata.clear();
16420 flat_packed_send_udata.resize(0);
16421 // ---------------------------------------------------------------
16422 // END: Send the data to the root processor
16423 // ---------------------------------------------------------------
16424
16425 // Container where root stores the info. that will be sent to all
16426 // processors. This includes the number of global nodes, the
16427 // number of names for each global node and the names
16428 Vector<unsigned> flat_packed_root_send_receive_udata;
16429
16430 // ---------------------------------------------------------------
16431 // BEGIN: Unpackage the info. received on root. Compute the alias
16432 // of the nodes
16433 // ---------------------------------------------------------------
16434 if (my_rank == root_processor)
16435 {
16436 // Compute all the names of a node
16437 // root_global_node_name[x][ ][ ] Global node number
16438 // root_global_node_name[ ][x][ ] Global node names
16439 // root_global_node_name[ ][ ][x] Global node info.
16440 Vector<Vector<Vector<unsigned>>> root_global_node_names;
16441
16442 // Store the info. extracted from the flat package sent to
16443 // root
16444 // root_local_node_names[x][ ] Node name
16445 // root_local_node_names[ ][x] Node info
16446 Vector<Vector<unsigned>> root_local_node_names;
16447
16448 // Extract all the node names
16449 unsigned rcounter = 0;
16450 // loop over the processors
16451 for (unsigned iproc = 0; iproc < nproc; iproc++)
16452 {
16453 // Get the number of node names received from iproc
16454 const unsigned n_local_names_iproc = root_n_names_per_processor[iproc];
16455 for (unsigned i = 0; i < n_local_names_iproc; i++)
16456 {
16457 // Get the i-thnode name from iproc
16458 Vector<unsigned> node_name(n_info_per_node_name);
16459 for (unsigned j = 0; j < n_info_per_node_name; j++)
16460 {
16461 node_name[j] = root_flat_packed_receive_udata[rcounter++];
16462 }
16463
16464 // Add the i-th node name
16465 root_local_node_names.push_back(node_name);
16466
16467 } // for (i < n_local_names_iproc)
16468
16469 } // for (iproc < nproc)
16470
16471 // Get the number of node names received
16472 const unsigned n_root_local_node_names = root_local_node_names.size();
16473
16474 // For each name of the node identify the position of its
16475 // counter-part
16476
16477 // Given a node name on the iproc,
16478 // (iproc, jproc, ishd_bnd, idx, local_node_number1)
16479 // its counter part must live in jproc, so we look for the
16480 // node name
16481 // (jproc, iproc, ishd_bnd, idx, local_node_number2)
16482
16483 // Store the index of the node name counter-part
16484 Vector<unsigned> node_name_counter_part(n_root_local_node_names);
16485
16486 // Keep track of the names of nodes already done
16487 std::map<Vector<unsigned>, bool> done_name;
16488
16489 // loop over the names of the nodes received from all
16490 // processors
16491 for (unsigned i = 0; i < n_root_local_node_names; i++)
16492 {
16493 // Get the i-th node name
16494 Vector<unsigned> node_name = root_local_node_names[i];
16495
16496 // Check if this name node has been already done
16497 if (!done_name[node_name])
16498 {
16499 // Mark it as done
16500 done_name[node_name] = true;
16501#ifdef PARANOID
16502 // Flag to indicate the counter-part name node was
16503 // found
16504 bool found_both_names_node = false;
16505#endif
16506 // Find the counter-part name node (start from j+1
16507 // since all previous have been found, otherwise we
16508 // would not be here)
16509 for (unsigned j = i + 1; j < n_root_local_node_names; j++)
16510 {
16511 Vector<unsigned> node_name_r = root_local_node_names[j];
16512
16513 // Check if this name node has been already done
16514 if (!done_name[node_name_r])
16515 {
16516 // Check whether this node is the
16517 // counter-part of the current name node
16518 if (node_name[0] == node_name_r[1] &&
16519 node_name[1] == node_name_r[0] &&
16520 node_name[2] == node_name_r[2] &&
16521 node_name[3] == node_name_r[3])
16522 {
16523 // Mark the name as node
16524 done_name[node_name_r] = true;
16525 // Store the index of the counter-part of
16526 // the current node name
16527 node_name_counter_part[i] = j;
16528 // ... and indicate the current node name
16529 // as the index of the counter-part
16530 node_name_counter_part[j] = i;
16531#ifdef PARANOID
16532 // The node has been found
16533 found_both_names_node = true;
16534#endif
16535 // Break the loop to find the
16536 // counter-part
16537 break;
16538 }
16539
16540 } // if (!done_name[node_name_r])
16541
16542 } // for (j < n_root_local_node_names)
16543#ifdef PARANOID
16544 // Check whether the node counter-part was found
16545 if (!found_both_names_node)
16546 {
16547 std::ostringstream error_message;
16548 error_message << "The counter-part of the current name node was "
16549 << "not found,\nthe current node name is:\n"
16550 << "iproc:(" << node_name[0] << ")\n"
16551 << "jproc:(" << node_name[1] << ")\n"
16552 << "ishd_bnd:(" << node_name[2] << ")\n"
16553 << "index:(" << node_name[3] << ")\n";
16554 throw OomphLibError(error_message.str(),
16555 OOMPH_CURRENT_FUNCTION,
16556 OOMPH_EXCEPTION_LOCATION);
16557 } // if (!found_both_names_node)
16558#endif
16559
16560 } // if (!done_name[node_name])
16561
16562 } // for (i < n_root_local_node_names)
16563
16564 // -----------------------------------------------------------
16565 // Look for all the names of each node received and store them
16566 // in the "global node names" container
16567
16568 // Keep track of the names of nodes already done
16569 done_name.clear();
16570 // loop over the names of the nodes received from all
16571 // processors
16572 for (unsigned i = 0; i < n_root_local_node_names; i++)
16573 {
16574 // Get the i-th node name
16575 Vector<unsigned> node_name = root_local_node_names[i];
16576
16577 // Check if this name node has been already done
16578 if (!done_name[node_name])
16579 {
16580 // Store all the names of the current node
16581 Vector<Vector<unsigned>> all_node_names;
16582
16583 // Add the name of the node as the initial node name
16584 all_node_names.push_back(node_name);
16585
16586 // Get the index of the counter-part
16587 unsigned idx_c = node_name_counter_part[i];
16588 // Get the counter-part of the node name
16589 Vector<unsigned> node_name_r = root_local_node_names[idx_c];
16590
16591 // Add the name of the counter-part of the node
16592 all_node_names.push_back(node_name_r);
16593 // We do not mark it as done since we are interested in
16594 // the names that the counter-part may generate
16595
16596 // Get the number of names for the current node (two at
16597 // the first time)
16598 unsigned n_current_names = all_node_names.size();
16599 // Counter to ensure to visit all the names of the current
16600 // node
16601 unsigned icounter = 0;
16602
16603 // Visit all the names of the current node
16604 while (icounter < n_current_names)
16605 {
16606 // Get the current node name
16607 Vector<unsigned> current_node_name = all_node_names[icounter];
16608
16609 // Search for other names for the current name of the
16610 // node, but first check if this has been already
16611 // visited
16612 if (!done_name[current_node_name])
16613 {
16614 // Mark it as done
16615 done_name[current_node_name] = true;
16616
16617 // loop over the names of the nodes (start from the
16618 // j+1 position, all previous node names have all
16619 // their names already assigned)
16620 for (unsigned j = i + 1; j < n_root_local_node_names; j++)
16621 {
16622 // Get the j-th node name
16623 Vector<unsigned> other_node_name = root_local_node_names[j];
16624
16625 // Is this name node already done
16626 if (!done_name[other_node_name])
16627 {
16628 // Is this another name for the current name node?
16629 if ((current_node_name[0] == other_node_name[0]) &&
16630 (current_node_name[4] == other_node_name[4]))
16631 {
16632 // Mark it as done. If we search again using the
16633 // "other_node_name" as the current node name we
16634 // are not going to find new nodes to add
16635 done_name[other_node_name] = true;
16636 // Before adding it check that it is not already
16637 // part of the names of the node
16638 Vector<Vector<unsigned>>::iterator it =
16639 std::find(all_node_names.begin(),
16640 all_node_names.end(),
16641 other_node_name);
16642 if (it == all_node_names.end())
16643 {
16644 all_node_names.push_back(other_node_name);
16645 // Get the index of the counter-part
16646 unsigned k = node_name_counter_part[j];
16647 // Get the counter-part of the node name
16648 Vector<unsigned> other_node_name_r =
16649 root_local_node_names[k];
16650 // Add the name of the counter-part of the
16651 // node only if it has not been previously
16652 // done
16653 if (!done_name[other_node_name_r])
16654 {
16655 all_node_names.push_back(other_node_name_r);
16656 }
16657 }
16658
16659 } // // Is this another name for the current name
16660 // node?
16661
16662 } // if (!done_name[other_node_name])
16663
16664 } // for (j < n_root_local_node_names)
16665
16666 } // if (!done_name[current_node_name])
16667
16668 // Get the number of names
16669 n_current_names = all_node_names.size();
16670 // Increase the icounter to indicate we have visited the
16671 // current name of the node
16672 icounter++;
16673
16674 } // while(icounter < n_current_names)
16675
16676 // We now have all the names for the i-th global node
16677 root_global_node_names.push_back(all_node_names);
16678
16679 } // if (!done_name[node_name])
16680
16681 } // for (i < n_root_local_node_names)
16682
16683 // -------------------------------------------------------------
16684 // Prepare the info to be sent to all processors. The number
16685 // of global nodes, the number of names for each global node,
16686 // and their respective names
16687 // -------------------------------------------------------------
16688
16689 // Clear the container
16690 flat_packed_root_send_receive_udata.clear();
16691 // Get the number of global nodes
16692 const unsigned n_global_nodes = root_global_node_names.size();
16693 // ... and store this info. to be sent from root to all
16694 // processors
16695 flat_packed_root_send_receive_udata.push_back(n_global_nodes);
16696
16697 // loop over the nodes
16698 for (unsigned i = 0; i < n_global_nodes; i++)
16699 {
16700 // Get the names of the i-th global node
16701 Vector<Vector<unsigned>> global_inode_names = root_global_node_names[i];
16702 // Get the number of names for the i-th global node
16703 const unsigned n_names_global_inode = global_inode_names.size();
16704 // ... and store this info. to be sent from root to all
16705 // processors
16706 flat_packed_root_send_receive_udata.push_back(n_names_global_inode);
16707 // loop over the names of the global i-th node
16708 for (unsigned j = 0; j < n_names_global_inode; j++)
16709 {
16710 // loop over the info. associated with each name
16711 for (unsigned k = 0; k < n_info_per_node_name; k++)
16712 {
16713 // Store the name info. of the current name in the
16714 // container to be sent from root to all processors
16715 flat_packed_root_send_receive_udata.push_back(
16716 global_inode_names[j][k]);
16717 } // for (k < n_info_per_node_name)
16718
16719 } // for (j < n_names_inode)
16720
16721 } // for (i < n_global_nodes)
16722
16723 } // if (my_rank == root_processor)
16724
16725 // ----------------------------------------------------------------
16726 // END: Unpackage the info. received on root. Compute the alias
16727 // of the nodes and prepare the info. to be sent back from
16728 // root to all processors
16729 // ----------------------------------------------------------------
16730
16731 // ---------------------------------------------------------------
16732 // BEGIN: Send the info. back to all processors, unpackage the
16733 // info. and create the map from node name to global node
16734 // index
16735 // ---------------------------------------------------------------
16736 // The number of data that root send to other processors.
16737 unsigned root_n_udata_sent_to_all_proc =
16738 flat_packed_root_send_receive_udata.size();
16739
16740 MPI_Bcast(&root_n_udata_sent_to_all_proc, // Data to send and
16741 // receive
16742 1,
16743 MPI_UNSIGNED,
16744 root_processor,
16745 comm_pt->mpi_comm());
16746
16747 // Resize the container if this is a processor that receives data
16748 if (my_rank != root_processor)
16749 {
16750 flat_packed_root_send_receive_udata.resize(root_n_udata_sent_to_all_proc);
16751 }
16752
16753 // Send the info. from root and receive it on all processors
16754 MPI_Bcast(&flat_packed_root_send_receive_udata[0], // Info. sent
16755 // from root to
16756 // all
16757 // processors
16758 root_n_udata_sent_to_all_proc, // Number of data sent
16759 // from root to each
16760 // procesor
16761 MPI_UNSIGNED,
16762 root_processor, // The processor that sends all the info.
16763 comm_pt->mpi_comm());
16764
16765 // Counter to extract the info.
16766 counter = 0;
16767 // Read the number of global nodes
16768 const unsigned n_global_nodes =
16769 flat_packed_root_send_receive_udata[counter++];
16770 // Store the global names of the nodes
16771 // global_node_name[x][ ][ ] Global node number
16772 // global_node_name[ ][x][ ] Global node names
16773 // global_node_name[ ][ ][x] Global node info.
16774 // Vector<Vector<Vector<unsigned> > > global_node_names(n_global_nodes);
16775 // Resize the input vector
16776 global_node_names.resize(n_global_nodes);
16777 // Now loop until all global nodes info. has been read
16778 unsigned n_read_global_nodes = 0;
16779 while (n_read_global_nodes < n_global_nodes)
16780 {
16781 // Read the number of names for the current global node
16782 const unsigned n_names_global_inode =
16783 flat_packed_root_send_receive_udata[counter++];
16784 // Counter for the global node
16785 const unsigned i = n_read_global_nodes;
16786 // Resize the container
16787 global_node_names[i].resize(n_names_global_inode);
16788 // loop over the names of the global inode
16789 for (unsigned j = 0; j < n_names_global_inode; j++)
16790 {
16791 // Resize the container
16792 global_node_names[i][j].resize(n_info_per_node_name);
16793 // loop over the info. of the j-th node name of the i-th
16794 // global node
16795 for (unsigned k = 0; k < n_info_per_node_name; k++)
16796 {
16797 // Read the k-th node info. from the j-th node name of
16798 // the i-th global node
16799 global_node_names[i][j][k] =
16800 flat_packed_root_send_receive_udata[counter++];
16801
16802 } // for (k < n_info_per_node_name)
16803
16804 // Create the map from the node name to the global node
16805 // index
16806 Vector<unsigned> node_name(n_info_per_node_name - 1);
16807 node_name[0] = global_node_names[i][j][0];
16808 node_name[1] = global_node_names[i][j][1];
16809 node_name[2] = global_node_names[i][j][2];
16810 node_name[3] = global_node_names[i][j][3];
16811 // Do not add the local index since it will not longer be
16812 // used. Additionally, we will not know the local node
16813 // index outside this method
16814 // node_name[4] = global_node_names[i][j][4];
16815 node_name_to_global_index[node_name] = i;
16816
16817 } // for (j < n_names_global_inode)
16818
16819 // Increase the counter for read global nodes
16820 n_read_global_nodes++;
16821
16822 } // while (n_read_global_nodes < n_global_nodes)
16823
16824#ifdef PARANOID
16825 // Check we have read all the info.
16826 if (counter != root_n_udata_sent_to_all_proc)
16827 {
16828 std::ostringstream error_stream;
16829 error_stream
16830 << "The info. received from root regarding the global names of "
16831 << "the nodes\nwas not completely read.\n"
16832 << "The number of data sent/received from root is: ("
16833 << root_n_udata_sent_to_all_proc << ")\n"
16834 << "The number of data read from the received info. is: (" << counter
16835 << ")\n\n";
16836 throw OomphLibError(
16837 error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
16838 } // if (counter != root_n_udata_sent_to_all_proc)
16839#endif
16840
16841 // ---------------------------------------------------------------
16842 // END: Send the info. back to all processors, unpackage the info.
16843 // and create the map from node name to global node index
16844 // ---------------------------------------------------------------
16845
16846 // ---------------------------------------------------------------
16847 // BEGIN: Add the info. from the global node names into the
16848 // info. of the local node names. We do this because the
16849 // local node names have pointers to the nodes.
16850 // Additionally, create a map from the node name to the
16851 // index of its global node
16852 // ---------------------------------------------------------------
16853
16854 // Resize the global shared node pointers container
16855 global_shared_node_pt.resize(n_global_nodes, 0);
16856
16857 // loop over the number of global nodes
16858 for (unsigned i = 0; i < n_global_nodes; i++)
16859 {
16860 // Flag to indicate that the iglobal node is part of the nodes
16861 // on the current processor
16862 bool is_this_a_local_node_name = false;
16863 unsigned local_node_number;
16864 // Get the number of names of the i-th global node
16865 const unsigned n_names_global_inode = global_node_names[i].size();
16866 // loop over the names of the i-th global node
16867 for (unsigned j = 0; j < n_names_global_inode; j++)
16868 {
16869 // Get the node name info.
16870 const unsigned iproc = global_node_names[i][j][0];
16871 local_node_number = global_node_names[i][j][4];
16872
16873 // Check if this node name lives on this processor
16874 if (my_rank == iproc)
16875 {
16876 // The node is part of the local node names
16877 is_this_a_local_node_name = true;
16878 // Break
16879 break;
16880 } // if (my_rank == iproc)
16881
16882 } // for (j < n_names_global_inode)
16883
16884 // If the node is part of the local nodes then add the
16885 // additional names of the node in the local container
16886 if (is_this_a_local_node_name)
16887 {
16888#ifdef PARANOID
16889 // Check that the global node include at least all the names
16890 // of the node on this processor
16891 const unsigned n_names_local_node =
16892 local_node_names[local_node_number].size();
16893 unsigned n_names_found_on_global_name_node = 0;
16894#endif
16895
16896 // Add the pointer of the node into the global shared node
16897 // pointers container
16898 global_shared_node_pt[i] = local_node_pt[local_node_number];
16899
16900 // Add all the global names of the node onto the local node
16901 // names
16902
16903 // loop again over the names of the i-th global node
16904 for (unsigned j = 0; j < n_names_global_inode; j++)
16905 {
16906 // Get the node name info.
16907 const unsigned iproc = global_node_names[i][j][0];
16908
16909 // Is this a node name on this processor?
16910 if (iproc != my_rank)
16911 {
16912 // Add the name
16913 local_node_names[local_node_number].push_back(
16914 global_node_names[i][j]);
16915 }
16916#ifdef PARANOID
16917 else
16918 {
16919 const unsigned jproc = global_node_names[i][j][1];
16920 const unsigned ishd_bnd = global_node_names[i][j][2];
16921 const unsigned idx = global_node_names[i][j][3];
16922 const unsigned n_local_node = global_node_names[i][j][4];
16923 // loop over the names of the local node
16924 for (unsigned k = 0; k < n_names_local_node; k++)
16925 {
16926 if ((local_node_names[local_node_number][k][0] == iproc) &&
16927 (local_node_names[local_node_number][k][1] == jproc) &&
16928 (local_node_names[local_node_number][k][2] == ishd_bnd) &&
16929 (local_node_names[local_node_number][k][3] == idx) &&
16930 (local_node_names[local_node_number][k][4] == n_local_node))
16931 {
16932 // Increase the number of local nodes found on the
16933 // global nodes
16934 n_names_found_on_global_name_node++;
16935 } // found global node on local nodes
16936
16937 } // for (k < n_names_local_node)
16938
16939 } // if (iproc != my_rank)
16940#endif
16941
16942 } // for (j < n_names_global_inode)
16943
16944#ifdef PARANOID
16945 // The number of local nodes names must be the same as the the
16946 // number of global nodes names associated with this processor
16947 // (my_rank, that start with iproc = my_rank)
16948 if (n_names_local_node != n_names_found_on_global_name_node)
16949 {
16950 std::ostringstream error_stream;
16951 error_stream << "The local node names corresponding to the local "
16952 << "node (" << local_node_number << ") were\n"
16953 << "not found on the global node names.\n\n"
16954 << "These are the names of the local node\n"
16955 << "Name k: iproc, jproc, ishd_bnd, idx. #node\n";
16956 for (unsigned k = 0; k < n_names_local_node; k++)
16957 {
16958 error_stream << "Name(" << k
16959 << "): " << local_node_names[local_node_number][k][0]
16960 << ", " << local_node_names[local_node_number][k][1]
16961 << ", " << local_node_names[local_node_number][k][2]
16962 << ", " << local_node_names[local_node_number][k][3]
16963 << ", " << local_node_names[local_node_number][k][4]
16964 << "\n";
16965 }
16966
16967 error_stream << "\n\nThese are the names of the global node\n"
16968 << "Name k: iproc, jproc, ishd_bnd, idx. #node\n";
16969 for (unsigned k = 0; k < n_names_global_inode; k++)
16970 {
16971 error_stream << "Name(" << k << "): " << global_node_names[i][k][0]
16972 << ", " << global_node_names[i][k][1] << ", "
16973 << global_node_names[i][k][2] << ", "
16974 << global_node_names[i][k][3] << ", "
16975 << global_node_names[i][k][4] << "\n";
16976 }
16977
16978 throw OomphLibError(error_stream.str(),
16979 OOMPH_CURRENT_FUNCTION,
16980 OOMPH_EXCEPTION_LOCATION);
16981 }
16982#endif
16983
16984 } // if (is_this_a_local_node_name)
16985
16986 } // for (i < n_global_nodes)
16987
16988 // ---------------------------------------------------------------
16989 // END: Add the info. from the global node names into the info.
16990 // of the local node names. We do this because the local
16991 // node names have pointers to the nodes
16992 // ---------------------------------------------------------------
16993
16994 // ---------------------------------------------------------------
16995 // BEGIN: Fill the data structure other_proc_shd_bnd_node_pt with
16996 // the local nodes.
16997 // ---------------------------------------------------------------
16998
16999 // Loop over the local nodes and fill the
17000 // other_proc_shd_bnd_node_pt container with the corresponding
17001 // info. NOTE: We are using the old size of the local node names,
17002 // before adding the names of the global nodes so we only loop
17003 // over the local nodes and not global.
17004
17005 // Compute the local shared boudary id
17006 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
17007
17008 // loop over the local nodes names
17009 for (unsigned i = 0; i < n_local_nodes; i++)
17010 {
17011 // Get the number of names for the i-th local node
17012 const unsigned n_names = local_node_names[i].size();
17013 // Get a pointer to the first name of the node found on this
17014 // processor (this ensures that the node lives on this
17015 // processor)
17016 Node* node_pt = local_node_pt[i];
17017 // loop over the names of the i-th local node and add an entry
17018 // to the other_proc_shd_bnd_node_pt structure
17019 for (unsigned j = 0; j < n_names; j++)
17020 {
17021 // Get the node name info.
17022 const unsigned iproc = local_node_names[i][j][0];
17023 const unsigned jproc = local_node_names[i][j][1];
17024 const unsigned ishd_bnd =
17025 local_node_names[i][j][2] - initial_shd_bnd_id;
17026 const unsigned index = local_node_names[i][j][3];
17027 // We can ignore the last entry, it was just used to compute
17028 // the global node number by the root processor
17029
17030 // Get the smallest processor number
17031 if (iproc < jproc)
17032 {
17033 other_proc_shd_bnd_node_pt[iproc][jproc][ishd_bnd][index] = node_pt;
17034 }
17035 else
17036 {
17037 other_proc_shd_bnd_node_pt[jproc][iproc][ishd_bnd][index] = node_pt;
17038 }
17039
17040 } // for (j < n_names)
17041
17042 } // for (i < n_local_node_names)
17043
17044 // ---------------------------------------------------------------
17045 // END: Fill the data structure other_proc_shd_bnd_node_pt with
17046 // the local nodes.
17047 // ---------------------------------------------------------------
17048 }
17049
17050 //======================================================================
17051 // Get the original boundaries to which is associated each
17052 // shared node, and send the info. to the related processors. We
17053 // need to do this so that at the reset of halo(ed) info. stage,
17054 // the info. is updated
17055 template<class ELEMENT>
17057 Vector<Vector<Vector<unsigned>>>& global_node_names,
17058 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
17059 Vector<Node*>& global_shared_node_pt)
17060 {
17061 // Get the rank and number of processors
17062 const unsigned nproc = this->communicator_pt()->nproc();
17063 const unsigned my_rank = this->communicator_pt()->my_rank();
17064
17065 // The number of nodes on shared boundaries
17066 const unsigned n_nodes_on_shd_bnds = global_node_names.size();
17067 // ---------------------------------------------------------
17068 // BEGIN: Get the shared nodes between each of processors
17069 // ---------------------------------------------------------
17070
17071 // Store the nodes on shared boundaries in this processor with other
17072 // processors
17073 Vector<std::set<Node*>> node_on_shd_bnd_pt(nproc);
17074
17075 // A map to get access to the global shared node number from the
17076 // node pointer
17077 std::map<Node*, unsigned> node_pt_to_global_shd_bnd_index;
17078
17079 // loop over the global nodes names and get only those in this
17080 // processor
17081 for (unsigned i = 0; i < n_nodes_on_shd_bnds; i++)
17082 {
17083 // Get the number of names of the current node on shared
17084 // boundaries
17085 const unsigned n_names = global_node_names[i].size();
17086 // loop over the names
17087 for (unsigned j = 0; j < n_names; j++)
17088 {
17089 // Store the node name
17090 Vector<unsigned> node_name(4);
17091 node_name[0] = global_node_names[i][j][0];
17092 node_name[1] = global_node_names[i][j][1];
17093 node_name[2] = global_node_names[i][j][2];
17094 node_name[3] = global_node_names[i][j][3];
17095
17096 // Check whether the node is in the current processor
17097 if (node_name[0] == my_rank)
17098 {
17099 // Check with which processor the node is shared
17100 const unsigned jproc = node_name[1];
17101
17102#ifdef PARANOID
17103 std::map<Vector<unsigned>, unsigned>::iterator it =
17104 node_name_to_global_index.find(node_name);
17105 if (it != node_name_to_global_index.end())
17106 {
17107 // Check whether the global node index correspond with that
17108 // of the current global node name
17109 if (i != (*it).second)
17110 {
17111 std::ostringstream error_message;
17112 error_message
17113 << "The global node number " << (*it).second
17114 << ") obtained from the current node\n"
17115 << "name is not the same as the current node number (" << i
17116 << ").\n\n"
17117 << "Node name:\n"
17118 << "iproc:" << node_name[0] << "\n"
17119 << "jproc:" << node_name[1] << "\n"
17120 << "shd_bnd_id:" << node_name[2] << "\n"
17121 << "index:" << node_name[3] << "\n\n";
17122 throw OomphLibError(error_message.str(),
17123 OOMPH_CURRENT_FUNCTION,
17124 OOMPH_EXCEPTION_LOCATION);
17125 }
17126 }
17127 else
17128 {
17129 std::ostringstream error_message;
17130 error_message
17131 << "The node name is not registerd as living in this processor.\n"
17132 << "Node name:\n"
17133 << "iproc:" << node_name[0] << "\n"
17134 << "jproc:" << node_name[1] << "\n"
17135 << "shd_bnd_id:" << node_name[2] << "\n"
17136 << "index:" << node_name[3] << "\n\n";
17137 throw OomphLibError(error_message.str(),
17138 OOMPH_CURRENT_FUNCTION,
17139 OOMPH_EXCEPTION_LOCATION);
17140 }
17141
17142#endif // #ifdef PARANOID
17143
17144 // Get the node pointer
17145 Node* node_pt = global_shared_node_pt[i];
17146
17147#ifdef PARANOID
17148 if (node_pt == 0)
17149 {
17150 std::ostringstream error_message;
17151 error_message << "There is not global shared node within this\n"
17152 << "global node number (" << i
17153 << "). The global shared\n"
17154 << "node pointer is null\n\n";
17155 throw OomphLibError(error_message.str(),
17156 OOMPH_CURRENT_FUNCTION,
17157 OOMPH_EXCEPTION_LOCATION);
17158 }
17159#endif // #ifdef PARANOID
17160
17161 // Add the node to the nodes on shared boundaries in this
17162 // processor
17163 node_on_shd_bnd_pt[jproc].insert(node_pt);
17164
17165 // And store the global node index
17166 node_pt_to_global_shd_bnd_index[node_pt] = i;
17167
17168 } // if (node_name[0]==my_rank)
17169 else if (node_name[1] == my_rank)
17170 {
17171 // Check with which processor the node is shared
17172 const unsigned jproc = node_name[0];
17173
17174#ifdef PARANOID
17175 std::map<Vector<unsigned>, unsigned>::iterator it =
17176 node_name_to_global_index.find(node_name);
17177 if (it != node_name_to_global_index.end())
17178 {
17179 // Check whether the global node index correspond with that
17180 // of the current global node name
17181 if (i != (*it).second)
17182 {
17183 std::ostringstream error_message;
17184 error_message
17185 << "The global node number " << (*it).second
17186 << ") obtained from the current node\n"
17187 << "name is not the same as the current node number (" << i
17188 << ").\n\n"
17189 << "Node name:\n"
17190 << "iproc:" << node_name[0] << "\n"
17191 << "jproc:" << node_name[1] << "\n"
17192 << "shd_bnd_id:" << node_name[2] << "\n"
17193 << "index:" << node_name[3] << "\n\n";
17194 throw OomphLibError(error_message.str(),
17195 OOMPH_CURRENT_FUNCTION,
17196 OOMPH_EXCEPTION_LOCATION);
17197 }
17198 }
17199 else
17200 {
17201 std::ostringstream error_message;
17202 error_message
17203 << "The node name is not registerd as living in this processor.\n"
17204 << "Node name:\n"
17205 << "iproc:" << node_name[0] << "\n"
17206 << "jproc:" << node_name[1] << "\n"
17207 << "shd_bnd_id:" << node_name[2] << "\n"
17208 << "index:" << node_name[3] << "\n\n";
17209 throw OomphLibError(error_message.str(),
17210 OOMPH_CURRENT_FUNCTION,
17211 OOMPH_EXCEPTION_LOCATION);
17212 }
17213
17214#endif // #ifdef PARANOID
17215
17216 // Get the node pointer
17217 Node* node_pt = global_shared_node_pt[i];
17218
17219#ifdef PARANOID
17220 if (node_pt == 0)
17221 {
17222 std::ostringstream error_message;
17223 error_message << "There is not global shared node within this\n"
17224 << "global node number (" << i
17225 << "). The global shared\n"
17226 << "node pointer is null\n\n";
17227 throw OomphLibError(error_message.str(),
17228 OOMPH_CURRENT_FUNCTION,
17229 OOMPH_EXCEPTION_LOCATION);
17230 }
17231#endif // #ifdef PARANOID
17232
17233 // Add the node to the nodes on shared boundaries in this
17234 // processor
17235 node_on_shd_bnd_pt[jproc].insert(node_pt);
17236
17237 // And store the global node index
17238 node_pt_to_global_shd_bnd_index[node_pt] = i;
17239 }
17240
17241 } // for (j < n_names)
17242
17243 } // for (i < n_nodes_on_shd_bnds)
17244
17245 // ---------------------------------------------------------
17246 // END: Get the shared nodes between each of processors
17247 // ---------------------------------------------------------
17248
17249 // ---------------------------------------------------------
17250 // BEGIN: Get the original boundaries associated to each
17251 // node on a shared boundary
17252 // ---------------------------------------------------------
17253
17254 // Store the global shared node number
17255 Vector<Vector<unsigned>> global_node_on_shared_bound(nproc);
17256 // Store the boundaries associated with the global shared node
17257 // number
17258 Vector<Vector<Vector<unsigned>>> global_node_original_boundaries(nproc);
17259 // Store the zeta boundary coordinate of the nodes on original
17260 // boundaries
17261 Vector<Vector<Vector<double>>> global_node_zeta_coordinate(nproc);
17262
17263 // loop over the processors
17264 for (unsigned iproc = 0; iproc < nproc; iproc++)
17265 {
17266 // Get the nodes added to be shared with the iproc processor
17267 std::set<Node*> nodes_shared_pt = node_on_shd_bnd_pt[iproc];
17268
17269 // loop over the nodes
17270 for (std::set<Node*>::iterator it = nodes_shared_pt.begin();
17271 it != nodes_shared_pt.end();
17272 it++)
17273 {
17274 // Get the node
17275 Node* node_pt = (*it);
17276 // Store the boundaries on which it is stored
17277 Vector<unsigned> on_original_boundaries;
17278 // For each boundary get the corresponding z value of the node
17279 // on the boundary
17280 Vector<double> zeta_coordinate;
17281 // Get the number of boudandaries
17282 const unsigned n_bnd = this->initial_shared_boundary_id();
17283 // loop over the boundaries and register the boundaries to which
17284 // it is associated
17285 for (unsigned bb = 0; bb < n_bnd; bb++)
17286 {
17287 // Is the node on original boundary bb?
17288 if (node_pt->is_on_boundary(bb))
17289 {
17290 // Then save it as being on boundary bb
17291 on_original_boundaries.push_back(bb);
17292 // Get the boundary coordinate
17293 Vector<double> zeta(1);
17294 node_pt->get_coordinates_on_boundary(bb, zeta);
17295 // Save the boundary coordinate
17296 zeta_coordinate.push_back(zeta[0]);
17297 }
17298
17299 } // for (bb < n_bnd)
17300
17301 // Is the node on an original boundary
17302 if (on_original_boundaries.size() > 0)
17303 {
17304 // Get the global shared node number
17305 std::map<Node*, unsigned>::iterator it_index =
17306 node_pt_to_global_shd_bnd_index.find(node_pt);
17307#ifdef PARANOID
17308 if (it_index == node_pt_to_global_shd_bnd_index.end())
17309 {
17310 std::ostringstream error_message;
17311 error_message
17312 << "We could not find the global shared node index associated\n"
17313 << "with the node pointer with vertices coordinates:\n"
17314 << "(" << node_pt->x(0) << ", " << node_pt->x(1) << ")\n\n";
17315 throw OomphLibError(error_message.str(),
17316 OOMPH_CURRENT_FUNCTION,
17317 OOMPH_EXCEPTION_LOCATION);
17318 }
17319#endif
17320 // The global shared node index
17321 const unsigned global_shared_node_number = (*it_index).second;
17322 // Store the global shared node number
17323 global_node_on_shared_bound[iproc].push_back(
17324 global_shared_node_number);
17325 // And store the original boundaries to which it is associated
17326 global_node_original_boundaries[iproc].push_back(
17327 on_original_boundaries);
17328 // and the corresponding zeta coordinate
17329 global_node_zeta_coordinate[iproc].push_back(zeta_coordinate);
17330 }
17331
17332 } // loop over nodes on shared boundaries with iproc
17333
17334 } // for (iproc < nproc)
17335
17336 // ---------------------------------------------------------
17337 // END: Get the original boundaries associated to each
17338 // node on a shared boundary
17339 // ---------------------------------------------------------
17340
17341 // ---------------------------------------------------------
17342 // BEGIN: Send the info. to the corresponding processors,
17343 // package the info, send it and receive it in the
17344 // corresponding processor, unpackage and set the
17345 // boundaries associated with the received nodes
17346 // ---------------------------------------------------------
17347
17348 // Get the communicator of the mesh
17349 OomphCommunicator* comm_pt = this->communicator_pt();
17350
17351 // Set MPI info
17352 MPI_Status status;
17353 MPI_Request request;
17354
17355 // loop over the processors
17356 for (unsigned iproc = 0; iproc < nproc; iproc++)
17357 {
17358 // The number of nodes shared between the pair of processors
17359 const unsigned n_shd_nodes_my_rank_iproc =
17360 node_on_shd_bnd_pt[iproc].size();
17361
17362 // Are there shared nodes between these pair of processors
17363 // (my_rank, iproc)? Also ensure not to send info. within myself
17364 if (n_shd_nodes_my_rank_iproc > 0 && iproc != my_rank)
17365 {
17366 // The flat package to send the info, to the iproc processor
17367 Vector<unsigned> flat_package_unsigned_send;
17368 // The very first entry is the number of nodes shared by the
17369 // pair of processors (my_rank, iproc)
17370 flat_package_unsigned_send.push_back(n_shd_nodes_my_rank_iproc);
17371
17372 // Get the number of shared nodes on original boundaries
17373 const unsigned n_global_shared_node_on_original_boundary =
17374 global_node_on_shared_bound[iproc].size();
17375
17376 // The second data is the number of shared nodes on original
17377 // boundaries
17378 flat_package_unsigned_send.push_back(
17379 n_global_shared_node_on_original_boundary);
17380
17381 // ... also send the zeta coordinates associated with the
17382 // original boundaries
17383 Vector<double> flat_package_double_send;
17384
17385 // loop over the nodes shared between this pair of processors
17386 for (unsigned i = 0; i < n_global_shared_node_on_original_boundary; i++)
17387 {
17388 // Get the global shared node index
17389 const unsigned global_shared_node_index =
17390 global_node_on_shared_bound[iproc][i];
17391
17392 // Put in the package the shared node index of the current
17393 // node
17394 flat_package_unsigned_send.push_back(global_shared_node_index);
17395
17396 // Get the original boundaries to which the node is associated
17397 Vector<unsigned> on_original_boundaries =
17398 global_node_original_boundaries[iproc][i];
17399
17400 // Get the associated zeta boundary coordinates
17401 Vector<double> zeta_coordinate =
17402 global_node_zeta_coordinate[iproc][i];
17403
17404 // Get the number of original boundaries to which the node is
17405 // associated
17406 const unsigned n_original_boundaries = on_original_boundaries.size();
17407
17408 // Put in the package the number of original boundaries the
17409 // node is associated
17410 flat_package_unsigned_send.push_back(n_original_boundaries);
17411
17412 // loop over the original boundaries ids and include them in
17413 // the package
17414 for (unsigned j = 0; j < n_original_boundaries; j++)
17415 {
17416 // Put in the package each of the original boundaries to
17417 // which it is associated
17418 flat_package_unsigned_send.push_back(on_original_boundaries[j]);
17419 // The zeta coordinate on the boundary
17420 flat_package_double_send.push_back(zeta_coordinate[j]);
17421 } // for (j < n_original_boundaries)
17422
17423 } // for (i < n_global_shared_node_on_original_boundary)
17424
17425 // Send data UNSIGNED -----------------------------------------
17426 // Get the size of the package to communicate to the iproc
17427 // processor
17428 const unsigned n_udata_send = flat_package_unsigned_send.size();
17429 int n_udata_send_int = n_udata_send;
17430
17431 // Send/receive data to/from iproc processor
17432 MPI_Isend(&n_udata_send_int,
17433 1,
17434 MPI_UNSIGNED,
17435 iproc,
17436 1,
17437 comm_pt->mpi_comm(),
17438 &request);
17439
17440 int n_udata_received_int = 0;
17441 MPI_Recv(&n_udata_received_int,
17442 1,
17443 MPI_UNSIGNED,
17444 iproc,
17445 1,
17446 comm_pt->mpi_comm(),
17447 &status);
17448 MPI_Wait(&request, MPI_STATUS_IGNORE);
17449
17450 if (n_udata_send != 0)
17451 {
17452 MPI_Isend(&flat_package_unsigned_send[0],
17453 n_udata_send,
17454 MPI_UNSIGNED,
17455 iproc,
17456 2,
17457 comm_pt->mpi_comm(),
17458 &request);
17459 }
17460
17461 const unsigned n_udata_received =
17462 static_cast<unsigned>(n_udata_received_int);
17463
17464 // Where to receive the data from the iproc processor
17465 Vector<unsigned> flat_package_unsigned_receive(n_udata_received);
17466
17467 if (n_udata_received != 0)
17468 {
17469 MPI_Recv(&flat_package_unsigned_receive[0],
17470 n_udata_received,
17471 MPI_UNSIGNED,
17472 iproc,
17473 2,
17474 comm_pt->mpi_comm(),
17475 &status);
17476 }
17477
17478 if (n_udata_send != 0)
17479 {
17480 MPI_Wait(&request, MPI_STATUS_IGNORE);
17481 }
17482
17483 // Send data DOUBLE -----------------------------------------
17484 // Get the size of the package to communicate to the iproc
17485 // processor
17486 const unsigned n_ddata_send = flat_package_double_send.size();
17487 int n_ddata_send_int = n_ddata_send;
17488
17489 // Send/receive data to/from iproc processor
17490 MPI_Isend(&n_ddata_send_int,
17491 1,
17492 MPI_UNSIGNED,
17493 iproc,
17494 1,
17495 comm_pt->mpi_comm(),
17496 &request);
17497
17498 int n_ddata_received_int = 0;
17499 MPI_Recv(&n_ddata_received_int,
17500 1,
17501 MPI_UNSIGNED,
17502 iproc,
17503 1,
17504 comm_pt->mpi_comm(),
17505 &status);
17506 MPI_Wait(&request, MPI_STATUS_IGNORE);
17507
17508 if (n_ddata_send != 0)
17509 {
17510 MPI_Isend(&flat_package_double_send[0],
17511 n_ddata_send,
17512 MPI_DOUBLE,
17513 iproc,
17514 2,
17515 comm_pt->mpi_comm(),
17516 &request);
17517 }
17518
17519 const unsigned n_ddata_received =
17520 static_cast<unsigned>(n_ddata_received_int);
17521
17522 // Where to receive the data from the iproc processor
17523 Vector<double> flat_package_double_receive(n_ddata_received);
17524
17525 if (n_ddata_received != 0)
17526 {
17527 MPI_Recv(&flat_package_double_receive[0],
17528 n_ddata_received,
17529 MPI_DOUBLE,
17530 iproc,
17531 2,
17532 comm_pt->mpi_comm(),
17533 &status);
17534 }
17535
17536 if (n_ddata_send != 0)
17537 {
17538 MPI_Wait(&request, MPI_STATUS_IGNORE);
17539 }
17540
17541 // Unpackage -------------------------------------------------
17542 // ... and associate the nodes to the corresponding original
17543 // boundaries
17544
17545 // The number of nodes to be received
17546 unsigned n_shared_nodes_received = flat_package_unsigned_receive[0];
17547
17548 // Increase and decrease the number of received shared nodes to
17549 // avoid the warning when compiling without PARANOID
17550 n_shared_nodes_received++;
17551 n_shared_nodes_received--;
17552
17553#ifdef PARANOID
17554 if (n_shd_nodes_my_rank_iproc != n_shared_nodes_received)
17555 {
17556 std::ostringstream error_message;
17557 error_message
17558 << "The number of shared nodes between the pair of processors is\n"
17559 << "not the same\n"
17560 << "N.shared nodes proc (" << my_rank << ") with proc (" << iproc
17561 << "): (" << n_shd_nodes_my_rank_iproc << "\n"
17562 << "N.shared nodes proc (" << iproc << ") with proc (" << my_rank
17563 << "): (" << n_shared_nodes_received << "\n\n"
17564 << "You should have got the same error in proc: (" << iproc
17565 << ")\n\n";
17566 throw OomphLibError(error_message.str(),
17567 OOMPH_CURRENT_FUNCTION,
17568 OOMPH_EXCEPTION_LOCATION);
17569 } // if (n_shd_nodes_my_rank_iproc != n_shared_nodes_received)
17570#endif
17571
17572 // Skip the number of nodes on shared boundaries on original
17573 // boundaries received (that is why next lines are commented)
17574
17575 // The number of nodes on shared boundaries on original
17576 // boundaries
17577 // const unsigned n_shared_nodes_on_original_boundaries_received =
17578 // flat_package_unsigned_receive[1];
17579
17580 // loop over the received info.
17581 unsigned current_index_data = 2;
17582 unsigned current_index_ddata = 0;
17583 while (current_index_data < n_udata_received)
17584 {
17585 // The global shared node number
17586 const unsigned global_shared_node_index =
17587 flat_package_unsigned_receive[current_index_data++];
17588
17589 // The pointer to the node
17590 Node* node_pt = 0;
17591
17592 // The number of original boundaries the node is associated
17593 // with
17594 const unsigned n_original_boundaries =
17595 flat_package_unsigned_receive[current_index_data++];
17596
17597 // Get the node pointer
17598 node_pt = global_shared_node_pt[global_shared_node_index];
17599#ifdef PARANOID
17600 if (node_pt == 0)
17601 {
17602 std::ostringstream error_message;
17603 error_message
17604 << "The global shared node (" << global_shared_node_index << ") "
17605 << "could not be found in this processor!!!\n"
17606 << "However, it was found in processor (" << iproc << "). The "
17607 << "data may be no synchronised,\ntherefore "
17608 << "we may be looking for a global shared node number that "
17609 << "do not\ncorrespond with the one that was sent by "
17610 << "processor (" << iproc << ")\n\n";
17611 throw OomphLibError(error_message.str(),
17612 OOMPH_CURRENT_FUNCTION,
17613 OOMPH_EXCEPTION_LOCATION);
17614 }
17615#endif // #ifdef PARANOID
17616
17617 // loop over the number of original boundaries and associate
17618 // the node to each of those boundaries
17619 for (unsigned i = 0; i < n_original_boundaries; i++)
17620 {
17621 // Get the original boundary to which the node is associated
17622 // with
17623 const unsigned original_bound_id =
17624 flat_package_unsigned_receive[current_index_data++];
17625
17626 // Associate the node with the boundary
17627 this->add_boundary_node(original_bound_id, node_pt);
17628
17629 // Get the zeta boundary coordinate
17630 Vector<double> zeta(1);
17631 zeta[0] = flat_package_double_receive[current_index_ddata++];
17632 node_pt->set_coordinates_on_boundary(original_bound_id, zeta);
17633 }
17634
17635 } // while(current_data < n_data_received)
17636
17637 } // if ((node_on_shd_bnd_pt(iproc) > 0) && iproc!=my_rank)
17638
17639 } // for (iproc < nproc)
17640
17641 // ---------------------------------------------------------
17642 // END: Send the info. to the corresponding processors,
17643 // package the info, send it and receive it in the
17644 // corresponding processor, unpackage and set the
17645 // boundaries associated with the received nodes
17646 // ---------------------------------------------------------
17647 }
17648
17649 //======================================================================
17650 // In charge of creating additional halo(ed) elements on those
17651 // processors that have no shared boundaries in common but have
17652 // shared nodes
17653 // ======================================================================
17654 template<class ELEMENT>
17656 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
17657 other_proc_shd_bnd_node_pt,
17658 Vector<Vector<Node*>>& iproc_currently_created_nodes_pt,
17659 Vector<Vector<Vector<unsigned>>>& global_node_names,
17660 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
17661 Vector<Node*>& global_shared_node_pt)
17662 {
17663 // Get the rank and number of processors
17664 const unsigned nproc = this->communicator_pt()->nproc();
17665 const unsigned my_rank = this->communicator_pt()->my_rank();
17666
17667 // ---------------------------------------------------------------
17668 // BEGIN: Create a map to check whether a node is on the global
17669 // shared nodes. Also set a map to obtain the global
17670 // shared node index (this index is the same as the global
17671 // node name)
17672 // ---------------------------------------------------------------
17673 std::map<Node*, bool> is_global_shared_node;
17674 std::map<Node*, unsigned> global_shared_node_index;
17675
17676 // Get the number of global shared nodes
17677 const unsigned n_global_shared_nodes = global_shared_node_pt.size();
17678 // loop over the global shared nodes
17679 for (unsigned i = 0; i < n_global_shared_nodes; i++)
17680 {
17681 // Get the node
17682 Node* node_pt = global_shared_node_pt[i];
17683 // Indicate this is a shared global node
17684 is_global_shared_node[node_pt] = true;
17685 // Set the map to obtain the index of the global shared node
17686 global_shared_node_index[node_pt] = i;
17687
17688 } // for (i < n_global_shared_nodes)
17689
17690 // ---------------------------------------------------------------
17691 // END: Create a map to check whether a node is on the global
17692 // shared nodes. Also set a map to obtain the global
17693 // shared node index (this index is the same as the global
17694 // node name)
17695 // ---------------------------------------------------------------
17696
17697 // ---------------------------------------------------------------
17698 // BEGIN: Loop over the haloed elements and check whether the nodes
17699 // on the haloed elements are part of the global shared
17700 // nodes. If that is the case then check whether the
17701 // element should be sent to the processors with which the
17702 // node is shared
17703 // ---------------------------------------------------------------
17704
17705 // Elements that may be sent to other processors
17706 Vector<std::set<GeneralisedElement*>> additional_elements_pt(nproc);
17707
17708 // loop over the processors
17709 for (unsigned iproc = 0; iproc < nproc; iproc++)
17710 {
17711 if (iproc != my_rank)
17712 {
17713 // Get the haloed element with iproc
17714 Vector<GeneralisedElement*> haloed_ele_pt =
17715 this->root_haloed_element_pt(iproc);
17716
17717 // Get the number of haloed elements
17718 const unsigned n_haloed_ele = this->nroot_haloed_element(iproc);
17719
17720 // loop over the haloed elements with iproc
17721 for (unsigned ihd = 0; ihd < n_haloed_ele; ihd++)
17722 {
17723 // A pointer to the generalised element
17724 GeneralisedElement* gele_pt = haloed_ele_pt[ihd];
17725 // Get the finite element representation of the element
17726 FiniteElement* ele_pt = dynamic_cast<FiniteElement*>(gele_pt);
17727 // Get the number of nodes
17728 const unsigned n_nodes = ele_pt->nnode();
17729 // loop over the nodes of the element
17730 for (unsigned n = 0; n < n_nodes; n++)
17731 {
17732 // Get the node
17733 Node* node_pt = ele_pt->node_pt(n);
17734 // Is the node a global shared node?
17735 if (is_global_shared_node[node_pt])
17736 {
17737 // Get the index of the global shared node
17738 const unsigned global_index = global_shared_node_index[node_pt];
17739 // Get the global names of the node
17740 Vector<Vector<unsigned>> iglobal_names =
17741 global_node_names[global_index];
17742
17743 // Get the number of names
17744 const unsigned n_names = iglobal_names.size();
17745 // loop over the names and check which processors share
17746 // this node (the processors to which the element may be
17747 // sent
17748 for (unsigned j = 0; j < n_names; j++)
17749 {
17750 // Get the processors to which the element should be
17751 // sent
17752 const unsigned proc1 = iglobal_names[j][0];
17753 const unsigned proc2 = iglobal_names[j][1];
17754 // Add the element to the set of additional elements to
17755 // sent from proc1 to proc2
17756 additional_elements_pt[proc1].insert(gele_pt);
17757 additional_elements_pt[proc2].insert(gele_pt);
17758
17759 } // for (j < n_names)
17760
17761 } // if (is_global_shared_node[node_pt])
17762
17763 } // for (n < n_nodes)
17764
17765 } // for (ihd < n_haloed_ele)
17766
17767 } // if (iproc!=my_rank)
17768
17769 } // for (iproc < nproc)
17770
17771 // ---------------------------------------------------------------
17772 // Now check whether the element should really be sent to the
17773 // indicated processors
17774
17775 // The elements from this (my_rank) processor that will be sent to
17776 // other processors
17777 Vector<Vector<FiniteElement*>> send_haloed_ele_pt(nproc);
17778
17779 // loop over the processors
17780 for (unsigned iproc = 0; iproc < nproc; iproc++)
17781 {
17782 if (iproc != my_rank)
17783 {
17784 // Get the set of element that may be sent to the iproc
17785 // processor
17786 std::set<GeneralisedElement*> iproc_ele_pt =
17787 additional_elements_pt[iproc];
17788 // loop over the element that may be sent to the iproc
17789 // processor
17790 for (std::set<GeneralisedElement*>::iterator it = iproc_ele_pt.begin();
17791 it != iproc_ele_pt.end();
17792 it++)
17793 {
17794 // Get a pointer to the element
17795 GeneralisedElement* gele_pt = (*it);
17796
17797 // Get the haloed element with iproc
17798 Vector<GeneralisedElement*> haloed_ele_pt =
17799 this->root_haloed_element_pt(iproc);
17800
17801 // Get the number of haloed elements
17802 const unsigned n_haloed_ele = this->nroot_haloed_element(iproc);
17803
17804 // Flag to indicate whether the element has been already sent
17805 // to the iproc processor
17806 bool send_ele_to_iproc_processor = true;
17807 // loop over the haloed elements with iproc and check whether
17808 // the element has been already sent to iproc (if it is
17809 // already a haloed element with iproc then it has been
17810 // already sent)
17811 for (unsigned ihd = 0; ihd < n_haloed_ele; ihd++)
17812 {
17813 // A pointer to the generalised element
17814 GeneralisedElement* ghd_ele_pt = haloed_ele_pt[ihd];
17815 if (gele_pt == ghd_ele_pt)
17816 {
17817 // Mark the element as not required to be sent
17818 send_ele_to_iproc_processor = false;
17819 // Break the loop that searchs for the element on the
17820 // haloed elements with iproc
17821 break;
17822 }
17823
17824 } // for (ihd < n_haloed_ele)
17825
17826 // Do we need to sent the element?
17827 if (send_ele_to_iproc_processor)
17828 {
17829 // Get the finite element representation of the element
17830 FiniteElement* ele_pt = dynamic_cast<FiniteElement*>(gele_pt);
17831 // Add the element to those that will be sent to the iproc
17832 // processor
17833 send_haloed_ele_pt[iproc].push_back(ele_pt);
17834 }
17835
17836 } // loop over the elements that may be sent to the iproc
17837 // processor
17838
17839 } // if (iproc!=my_rank)
17840
17841 } // for (iproc < nproc)
17842
17843 // ---------------------------------------------------------------
17844 // END: Loop over the haloed element and check whether the nodes
17845 // on the haloed elements are part of the global shared
17846 // nodes. If that is the case then check whether the element
17847 // should be sent to the processors with which the node is
17848 // shared
17849 // ---------------------------------------------------------------
17850
17851 // ============================================================
17852 // Now send the additional elements
17853 // ============================================================
17854 // Loop over the processors to send data
17855 for (unsigned iproc = 0; iproc < nproc; iproc++)
17856 {
17857 // There are no elements to send with myself
17858 if (iproc != my_rank)
17859 {
17860 // Get the number of additional haloed elements to send
17861 const unsigned n_additional_haloed_ele =
17862 send_haloed_ele_pt[iproc].size();
17863
17864 // Clear send and receive buffers
17865 Flat_packed_unsigneds.clear();
17866 Flat_packed_doubles.clear();
17867#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17869#endif
17870
17871 // The very first data of the flat packed is the number of
17872 // additional haloed elements, this will be the number of
17873 // additional halo elements to create on the receiver processor
17874 Flat_packed_unsigneds.push_back(n_additional_haloed_ele);
17875#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17876 std::stringstream junk;
17877 junk << "Number of haloed elements " << nhaloed_ele;
17878 Flat_packed_unsigneds_string.push_back(junk.str());
17879#endif
17880
17881 // Loop over the additioanl haloed elements
17882 for (unsigned e = 0; e < n_additional_haloed_ele; e++)
17883 {
17884 // Get pointer to the additional haloed element
17885 FiniteElement* ele_pt = send_haloed_ele_pt[iproc][e];
17886 const unsigned nroot_haloed_ele = this->nroot_haloed_element(iproc);
17887
17888 // Check if the element has been already added to the
17889 // halo(ed) scheme
17890
17891 // Get the generalised version of the element
17892 GeneralisedElement* gen_ele_pt = ele_pt;
17893 // Try to add the haloed element
17894 const unsigned haloed_ele_index =
17895 this->try_to_add_root_haloed_element_pt(iproc, gen_ele_pt);
17896
17897 // Was the element added or only returned the index of the
17898 // element
17899 if (nroot_haloed_ele == haloed_ele_index)
17900 {
17901 Flat_packed_unsigneds.push_back(1);
17902#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17904 "Haloed element needs to be constructed");
17905#endif
17906
17907 // Get additional info. related with the haloed element
17908 get_required_elemental_information_helper(iproc, ele_pt);
17909
17910 // Get the nodes on the element
17911 const unsigned nnodes = ele_pt->nnode();
17912 for (unsigned j = 0; j < nnodes; j++)
17913 {
17914 Node* node_pt = ele_pt->node_pt(j);
17915
17916 // Package the info. of the nodes
17917 // The destination processor goes in the arguments
17918 add_haloed_node_helper(iproc, node_pt);
17919
17920 } // for (j < nnodes)
17921
17922 } // add the element and send its nodes
17923 else // The haloed element already exists
17924 {
17925 Flat_packed_unsigneds.push_back(0);
17926#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17928 "Haloed element already exists");
17929#endif
17930 Flat_packed_unsigneds.push_back(haloed_ele_index);
17931#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17933 "Index of existing haloed element");
17934#endif
17935 } // else (next_haloed_ele == external_haloed_ele_index)
17936
17937 } // for (e < n_additional_haloed_ele)
17938
17939 // Send and received the additional haloed elements (all
17940 // processors send and receive)
17941
17942 // The processor to which send the elements
17943 int send_proc = static_cast<int>(iproc);
17944 // The processor from which receive the elements
17945 int recv_proc = static_cast<int>(iproc);
17946 send_and_receive_elements_nodes_info(send_proc, recv_proc);
17947
17948 // Reset the counters
17951
17952 // Get the number of additional halo element to be created
17953 const unsigned n_additional_halo_ele =
17955
17956#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17958 << " Number of elements need to be constructed "
17960 << std::endl;
17961#endif
17962
17963 // Create the additional halo elements
17964 for (unsigned e = 0; e < n_additional_halo_ele; e++)
17965 {
17966 // Create halo element from received info. of "iproc"
17967 // processor on the current processor
17968 create_halo_element(iproc,
17969 iproc_currently_created_nodes_pt[iproc],
17970 other_proc_shd_bnd_node_pt,
17971 global_node_names,
17972 node_name_to_global_index,
17973 global_shared_node_pt);
17974
17975 } // for (e < n_additional_halo_ele)
17976
17977 } // if (iproc != my_rank)
17978
17979 } // for (iproc < nproc)
17980 }
17981
17982 // *********************************************************************
17983 // Start communication functions
17984 // *********************************************************************
17985
17986 //========start of get_required_elemental_information_helper==============
17987 /// Helper function to get the required elemental information from
17988 /// an haloed element. This info. involves the association of the element
17989 /// to a boundary or region.
17990 //========================================================================
17991 template<class ELEMENT>
17993 ELEMENT>::get_required_elemental_information_helper(unsigned& iproc,
17994 FiniteElement* ele_pt)
17995 {
17996 // Check if the element is associated with the original boundaries
17997 const unsigned nbound = this->initial_shared_boundary_id();
17998
17999 // ------------------------------------------------------------------
18000 // Stores the information regarding the boundaries associated to the
18001 // element (it that is the case)
18002 Vector<unsigned> associated_boundaries;
18003 Vector<unsigned> face_index_on_boundary;
18004
18005 unsigned counter_face_indexes = 0;
18006
18007 for (unsigned b = 0; b < nbound; b++)
18008 {
18009 // Get the number of elements associated to boundary i
18010 const unsigned nboundary_ele = nboundary_element(b);
18011 for (unsigned e = 0; e < nboundary_ele; e++)
18012 {
18013 if (ele_pt == this->boundary_element_pt(b, e))
18014 {
18015 // Keep track of the boundaries associated to the element
18016 associated_boundaries.push_back(b);
18017 // Get the face index
18018 face_index_on_boundary.push_back(face_index_at_boundary(b, e));
18019 counter_face_indexes++;
18020#ifdef PARANOID
18021 if (counter_face_indexes > 2)
18022 {
18023 std::stringstream error_message;
18024 error_message
18025 << "A triangular element can not have more than two of its faces "
18026 << "on a boundary!!!\n\n";
18027 throw OomphLibError(error_message.str(),
18028 OOMPH_CURRENT_FUNCTION,
18029 OOMPH_EXCEPTION_LOCATION);
18030 }
18031#else
18032 // Already found 2 face indexes on the same boundary?
18033 if (counter_face_indexes == 2)
18034 {
18035 break;
18036 }
18037#endif // #ifdef PARANOID
18038
18039 } // if (ele_pt == this->boundary_element_pt(b,e))
18040
18041 } // (e < nboundary_ele)
18042
18043 } // (b < nbound)
18044
18045 // If the element is associated to any boundary then package all the
18046 // relevant info
18047 const unsigned nassociated_boundaries = associated_boundaries.size();
18048 if (nassociated_boundaries > 0)
18049 {
18050 Flat_packed_unsigneds.push_back(1);
18051#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18053 "The element is a boundary element");
18054#endif
18055 Flat_packed_unsigneds.push_back(nassociated_boundaries);
18056#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18057 std::stringstream junk;
18058 junk << "The elements is associated to " << nassociated_boundaries
18059 << " boundaries";
18060 Flat_packed_unsigneds_string.push_back(junk.str());
18061#endif
18062
18063 // Package the ids of the associated boundaries and the
18064 // corresponding face index for each boundary (if the element is a
18065 // corner element, it will have two faces associated to the
18066 // boundary)
18067 for (unsigned i = 0; i < nassociated_boundaries; i++)
18068 {
18069 unsigned b = associated_boundaries[i];
18070 Flat_packed_unsigneds.push_back(b);
18071#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18072 std::stringstream junk;
18073 junk << "Element associated to boundary " << b << " of "
18074 << nassociated_boundaries << " total associated boundaries";
18075 Flat_packed_unsigneds_string.push_back(junk.str());
18076#endif
18077 unsigned f = face_index_on_boundary[i];
18078 Flat_packed_unsigneds.push_back(f);
18079#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18080 std::stringstream junk2;
18081 junk2 << "Face index " << f << " for associated boundary " << b;
18082 Flat_packed_unsigneds_string.push_back(junk2.str());
18083#endif
18084 }
18085
18086 // If the element is associated to any boundary then we should
18087 // check if the mesh has regions, if that is the case then we need
18088 // to check to which region the boundary element does belong
18089
18090 // If the mesh has regions we should look for the element
18091 // associated to a boundary and a specified region
18092 Vector<Vector<unsigned>> associated_boundaries_and_regions;
18093 Vector<unsigned> face_index_on_boundary_and_region;
18094
18095 // Now check for the case when we have regions in the mesh
18096 const unsigned n_regions = this->nregion();
18097 if (n_regions > 1)
18098 {
18099 // Used to count the number of faces associated with
18100 // boundary-regions
18101 unsigned counter_face_indexes_in_regions = 0;
18102 // Loop over the boundaries
18103 for (unsigned b = 0; b < nbound; b++)
18104 {
18105 // Go through each region by getting the region id
18106 for (unsigned i_reg = 0; i_reg < n_regions; i_reg++)
18107 {
18108 // Get thre region id associated with the (i_reg)-th region
18109 const unsigned region_id =
18110 static_cast<unsigned>(this->Region_attribute[i_reg]);
18111
18112 // Loop over all elements associated with the current boundary
18113 // and the i_reg-th region and check if the element is part of
18114 // any region
18115 const unsigned nele_in_region =
18116 this->nboundary_element_in_region(b, region_id);
18117 for (unsigned ee = 0; ee < nele_in_region; ee++)
18118 {
18119 // Check if the boundary-region element is the same as the
18120 // element
18121 if (ele_pt ==
18122 this->boundary_element_in_region_pt(b, region_id, ee))
18123 {
18124 // Storage for the boundary and region associated to the
18125 // element
18126 Vector<unsigned> bound_and_region(2);
18127
18128 // Keep track of the boundaries associated to the element
18129 bound_and_region[0] = b;
18130 // Keep track of the regions associated to the element
18131 bound_and_region[1] = region_id;
18132 // Add the boundaries and regions in the storage to be
18133 // sent to other processors
18134 associated_boundaries_and_regions.push_back(bound_and_region);
18135 // Get the face index and keep track of it
18136 face_index_on_boundary_and_region.push_back(
18137 this->face_index_at_boundary_in_region(b, region_id, ee));
18138
18139 // Increase the number of faces of the element associated
18140 // to boundary-regions
18141 counter_face_indexes_in_regions++;
18142
18143#ifdef PARANOID
18144 if (counter_face_indexes_in_regions > 2)
18145 {
18146 std::stringstream error_message;
18147 error_message << "A triangular element can not have more "
18148 "than two of its\n"
18149 << "faces on a boundary!!!\n\n";
18150 throw OomphLibError(error_message.str(),
18151 OOMPH_CURRENT_FUNCTION,
18152 OOMPH_EXCEPTION_LOCATION);
18153 } // if (counter_face_indexes_in_regions > 2)
18154#endif
18155
18156 } // The element is a boundary-region element
18157
18158 } // for (ee < nele_in_region)
18159
18160 } // for (i_reg < n_regions)
18161
18162 } // for (b < nbound)
18163
18164 } // if (n_regions > 1)
18165
18166 // Now package the info. to be sent to other processors
18167 const unsigned nassociated_boundaries_and_regions =
18168 associated_boundaries_and_regions.size();
18169 if (nassociated_boundaries_and_regions > 0)
18170 {
18171 Flat_packed_unsigneds.push_back(1);
18172#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18174 "The element is associated to boundaries and regions");
18175#endif
18176
18177 Flat_packed_unsigneds.push_back(nassociated_boundaries_and_regions);
18178#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18179 std::stringstream junk;
18180 junk << "The element is associated to "
18181 << nassociated_boundaries_and_regions << " boundaries-regions";
18182 Flat_packed_unsigneds_string.push_back(junk.str());
18183#endif
18184
18185 // Package the ids of the associated boundaries, regions and the
18186 // corresponding face index for each boundary-region (if the
18187 // element is a corner element, it will have two faces
18188 // associated to the boundary-region)
18189 for (unsigned i = 0; i < nassociated_boundaries_and_regions; i++)
18190 {
18191 const unsigned b = associated_boundaries_and_regions[i][0];
18192 Flat_packed_unsigneds.push_back(b);
18193#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18194 std::stringstream junk;
18195 junk << "Element associated to boundary " << b << " of "
18196 << nassociated_boundaries_and_regions
18197 << " total associated boundaries-regions";
18198 Flat_packed_unsigneds_string.push_back(junk.str());
18199#endif
18200
18201 const unsigned r = associated_boundaries_and_regions[i][1];
18202 Flat_packed_unsigneds.push_back(r);
18203#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18204 std::stringstream junk2;
18205 junk2 << "Element associated to region " << r << " of "
18206 << nassociated_boundaries_and_regions
18207 << " total associated boundaries-regions";
18208 Flat_packed_unsigneds_string.push_back(junk2.str());
18209#endif
18210
18211 const unsigned f = face_index_on_boundary_and_region[i];
18212 Flat_packed_unsigneds.push_back(f);
18213#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18214 std::stringstream junk3;
18215 junk3 << "Face index " << f << " for associated boundary-region ("
18216 << b << "-" << r << ")";
18217 Flat_packed_unsigneds_string.push_back(junk3.str());
18218#endif
18219 } // for (i < nassociated_boundaries_and_regions)
18220 } // if (nassociated_boundaries_and_regions > 0)
18221 else
18222 {
18223 Flat_packed_unsigneds.push_back(0);
18224#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18226 "The element is NOT associated to boundaries and regions");
18227#endif
18228 } // else if (nassociated_boundaries_and_regions > 0)
18229 }
18230 else
18231 {
18232 Flat_packed_unsigneds.push_back(0);
18233#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18235 "The element is not associated to any original boundary");
18236#endif
18237 }
18238
18239 // ------------------------------------------------------------
18240 // Now review if the element is associated to a shared boundary
18241
18242 // Store the shared boundaries, and therefore the face indexes
18243 // associated to the element
18244 Vector<unsigned> associated_shared_boundaries;
18245 Vector<unsigned> face_index_on_shared_boundary;
18246
18247 // Get the shared boundaries in this processor
18248 Vector<unsigned> my_rank_shared_boundaries_ids;
18249 this->shared_boundaries_in_this_processor(my_rank_shared_boundaries_ids);
18250
18251 // Get the number of shared boundaries
18252 const unsigned nmy_rank_shd_bnd = my_rank_shared_boundaries_ids.size();
18253 // Loop over the shared boundaries
18254 for (unsigned i = 0; i < nmy_rank_shd_bnd; i++)
18255 {
18256 // Get the boundary id
18257 const unsigned sb = my_rank_shared_boundaries_ids[i];
18258
18259 // Get the number of elements associated to shared boundary sb
18260 const unsigned nboundary_ele = this->nshared_boundary_element(sb);
18261 for (unsigned e = 0; e < nboundary_ele; e++)
18262 {
18263 if (ele_pt == this->shared_boundary_element_pt(sb, e))
18264 {
18265 // Keep track of the boundaries associated to the element
18266 associated_shared_boundaries.push_back(sb);
18267 // Get the face index
18268 face_index_on_shared_boundary.push_back(
18269 this->face_index_at_shared_boundary(sb, e));
18270 }
18271 } // (e < nboundary_ele)
18272 } // (i < nmy_rank_shd_bnd)
18273
18274 // If the element is associated to a shared boundary then package
18275 // all the relevant info
18276 const unsigned nassociated_shared_boundaries =
18277 associated_shared_boundaries.size();
18278 if (nassociated_shared_boundaries > 0)
18279 {
18280 Flat_packed_unsigneds.push_back(3);
18281#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18283 "The element is a shared boundary element");
18284#endif
18285 Flat_packed_unsigneds.push_back(nassociated_shared_boundaries);
18286#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18287 std::stringstream junk;
18288 junk << "The elements is associated to " << nassociated_shared_boundaries
18289 << "shared boundaries";
18290 Flat_packed_unsigneds_string.push_back(junk.str());
18291#endif
18292
18293 // Package the ids of the associated boundaries
18294 for (unsigned i = 0; i < nassociated_shared_boundaries; i++)
18295 {
18296 const unsigned b = associated_shared_boundaries[i];
18297 Flat_packed_unsigneds.push_back(b);
18298#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18299 std::stringstream junk;
18300 junk << "Element associated to shared boundary " << b << " of "
18301 << nassociated_shared_boundaries << " total associated boundaries";
18302 Flat_packed_unsigneds_string.push_back(junk.str());
18303#endif
18304
18305 const unsigned f = face_index_on_shared_boundary[i];
18306 Flat_packed_unsigneds.push_back(f);
18307#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18308 std::stringstream junk2;
18309 junk2 << "Face index " << f << " for associated shared boundary " << b;
18310 Flat_packed_unsigneds_string.push_back(junk2.str());
18311#endif
18312 }
18313 }
18314 else
18315 {
18316 Flat_packed_unsigneds.push_back(0);
18317#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18319 "The element is not associated to any shared boundary");
18320#endif
18321 }
18322 }
18323
18324 //========start of get_required_nodal_information_helper==================
18325 /// Helper function to get the required nodal information from an
18326 /// haloed node so that a fully-functional halo node (and therefore element)
18327 /// can be created on the receiving process
18328 //========================================================================
18329 template<class ELEMENT>
18331 unsigned& iproc, Node* nod_pt)
18332 {
18333 unsigned my_rank = this->communicator_pt()->my_rank();
18334 const unsigned nproc = this->communicator_pt()->nproc();
18335
18336 // Tell the halo copy of this node how many values there are
18337 // [NB this may be different for nodes within the same element, e.g.
18338 // when using Lagrange multipliers]
18339 unsigned n_val = nod_pt->nvalue();
18340 Flat_packed_unsigneds.push_back(n_val);
18341#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18342 Flat_packed_unsigneds_string.push_back("Number of values");
18343#endif
18344
18345 unsigned n_dim = nod_pt->ndim();
18346
18347 // Default number of previous values to 1
18348 unsigned n_prev = 1;
18349 if (this->Time_stepper_pt != 0)
18350 {
18351 // Add number of history values to n_prev
18352 n_prev = this->Time_stepper_pt->ntstorage();
18353 }
18354
18355 // -----------------------------------------------------
18356 // Is the node on an original boundary?
18357 // Store the original boundaries where the node may be
18358 Vector<unsigned> original_boundaries;
18359 // Loop over the original boundaries of the mesh and check if live
18360 // on one of them
18361 const unsigned n_bnd = this->initial_shared_boundary_id();
18362 for (unsigned bb = 0; bb < n_bnd; bb++)
18363 {
18364 // Which boundaries (could be more than one) is it on?
18365 if (nod_pt->is_on_boundary(bb))
18366 {
18367 original_boundaries.push_back(bb);
18368 }
18369 }
18370
18371 const unsigned n_original_boundaries = original_boundaries.size();
18372 // Is the node on any original boundary?
18373 if (n_original_boundaries > 0)
18374 {
18375 // Indicate that the node is on an original boundary
18376 Flat_packed_unsigneds.push_back(2);
18377#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18379 "Node is on the original boundaries");
18380#endif
18381
18382 Flat_packed_unsigneds.push_back(n_original_boundaries);
18383#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18384 std::stringstream junk;
18385 junk << "Node is on " << n_original_boundaries << " original boundaries";
18386 Flat_packed_unsigneds_string.push_back(junk.str());
18387#endif
18388
18389 // Loop over the original boundaries the node is on
18390 for (unsigned i = 0; i < n_original_boundaries; i++)
18391 {
18392 Flat_packed_unsigneds.push_back(original_boundaries[i]);
18393#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18394 std::stringstream junk;
18395 junk << "Node is on boundary " << original_boundaries[i] << " of "
18396 << nb;
18397 Flat_packed_unsigneds_string.push_back(junk.str());
18398#endif
18399 // Get the boundary coordinate of the node
18400 Vector<double> zeta(1);
18401 nod_pt->get_coordinates_on_boundary(original_boundaries[i], zeta);
18402 Flat_packed_doubles.push_back(zeta[0]);
18403 }
18404 }
18405 else
18406 {
18407 // Indicate that the node is NOT on an original boundary
18408 Flat_packed_unsigneds.push_back(0);
18409#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18411 "Node is on any original boundary");
18412#endif
18413 }
18414
18415 // -------------------------------------------------------
18416 // Is the node on shared boundaries?
18417 bool node_on_shared_boundary = false;
18418 // Loop over the shared boundaries with the iproc processors and
18419 // check if live on one of them
18420 const unsigned n_shd_bnd = this->nshared_boundaries(my_rank, iproc);
18421 for (unsigned bb = 0; bb < n_shd_bnd; bb++)
18422 {
18423 // Get the boundary id
18424 unsigned i_bnd = this->shared_boundaries_ids(my_rank, iproc, bb);
18425 // Which boundaries (could be more than one) is it on?
18426 if (this->is_node_on_shared_boundary(i_bnd, nod_pt))
18427 {
18428 node_on_shared_boundary = true;
18429 break;
18430 }
18431 }
18432
18433 // If the node live on any of the shared boundaries with the iproc
18434 // processor then just get the node number according to the
18435 // sorted_shared_boundary_node_pt() scheme and send it accross
18436 if (node_on_shared_boundary)
18437 {
18438 Flat_packed_unsigneds.push_back(1);
18439#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18440 Flat_packed_unsigneds_string.push_back("Node is on shared boundary");
18441#endif
18442
18443 // Store the shared boundaries where the node is on
18444 Vector<unsigned> shd_boundaries;
18445 // Loop over the shared boundaries with the iproc processor
18446 for (unsigned bb = 0; bb < n_shd_bnd; bb++)
18447 {
18448 // Get the boundary id
18449 const unsigned i_bnd = this->shared_boundaries_ids(my_rank, iproc, bb);
18450 // Which boundaries (could be more than one) is it on?
18451 if (this->is_node_on_shared_boundary(i_bnd, nod_pt))
18452 {
18453 shd_boundaries.push_back(i_bnd);
18454 }
18455 }
18456
18457 // Get the number of shared boundaries the node is on
18458 const unsigned n_shd_bnd_is_on = shd_boundaries.size();
18459 // Send the number of shared boundaries the node is on
18460 Flat_packed_unsigneds.push_back(n_shd_bnd_is_on);
18461#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18462 std::stringstream junk;
18463 junk << "Node is on " << n_shd_bnd_is_on << " shared boundaries";
18464 Flat_packed_unsigneds_string.push_back(junk.str());
18465#endif
18466
18467 // Loop over the shared boundaries to send their ids
18468 for (unsigned i = 0; i < n_shd_bnd_is_on; i++)
18469 {
18470 Flat_packed_unsigneds.push_back(shd_boundaries[i]);
18471#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18472 std::stringstream junk;
18473 junk << "Node is on boundary " << shd_boundaries[i] << " of " << nb;
18474 Flat_packed_unsigneds_string.push_back(junk.str());
18475#endif
18476 }
18477
18478 // Given that the node is on at least one boundary get the index
18479 // of the node in one of the boundaries and send this index
18480 unsigned shared_boundary_id = shd_boundaries[0];
18481 // Get the number of nodes on the given shared boundary
18482 const unsigned n_nodes_on_shared_boundary =
18483 nsorted_shared_boundary_node(shared_boundary_id);
18484 // Store the index of the node on the shared boundary
18485 unsigned index_node_on_shared_boundary;
18486#ifdef PARANOID
18487 // Flag to know if the node has been found
18488 bool found_index_node_on_shared_boundary = false;
18489#endif
18490 // Loop over the nodes on the shared boundary to find the node
18491 for (unsigned i = 0; i < n_nodes_on_shared_boundary; i++)
18492 {
18493 // Get the i-th node on the shared boundary
18494 Node* shared_node_pt =
18495 sorted_shared_boundary_node_pt(shared_boundary_id, i);
18496 // Is the node we are looking for
18497 if (shared_node_pt == nod_pt)
18498 {
18499 // Store the index
18500 index_node_on_shared_boundary = i;
18501#ifdef PARANOID
18502 // Mark as found
18503 found_index_node_on_shared_boundary = true;
18504#endif
18505 break; // break
18506 }
18507
18508 } // for (i < nnodes_on_shared_boundary)
18509
18510#ifdef PARANOID
18511 if (!found_index_node_on_shared_boundary)
18512 {
18513 std::ostringstream error_message;
18514 error_message << "The index of the node on boundary ("
18515 << shared_boundary_id << ") was not found.\n"
18516 << "The node coordinates are (" << nod_pt->x(0) << ","
18517 << nod_pt->x(1) << ").\n";
18518 throw OomphLibError(
18519 error_message.str(),
18520 "RefineableTriangleMesh::get_required_nodal_information_helper()",
18521 OOMPH_EXCEPTION_LOCATION);
18522 }
18523#endif
18524 // Send the index of the node on the shared boundary
18525 Flat_packed_unsigneds.push_back(index_node_on_shared_boundary);
18526#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18527 std::stringstream junk2;
18528 junk2 << "Node index on boundary " << boundaries[0] << " is "
18529 << index_node_on_shared_boundary;
18530 Flat_packed_unsigneds_string.push_back(junk2.str());
18531#endif
18532
18533 } // if (node_on_shared_boundary)
18534 else
18535 {
18536 // The node is not on a shared boundary
18537 Flat_packed_unsigneds.push_back(0);
18538#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18540 "Node is not on a shared boundary");
18541#endif
18542 }
18543
18544 // ----------------------------------------------------------------
18545 // Is the node on any shared boundary where the receiver processor
18546 // is not involved?
18547
18548 // Now check if the node is on a shared boundary created by the
18549 // current processor (my_rank) and other processor different that
18550 // the iproc processor. This info. will help to complete the sending
18551 // of halo(ed) information between processors
18552
18553 // Flag to know if the node is on a shared boundary with other
18554 // processor
18555 bool node_on_shared_boundary_with_other_processors = false;
18556 // Count the number of other shared boundaries it could be on
18557 unsigned nshared_boundaries_with_other_processors_have_node = 0;
18558
18559 // Loop over the shared boundaries of the sent processor (my_rank)
18560 // and other processors (jproc)
18561 for (unsigned jproc = 0; jproc < nproc; jproc++)
18562 {
18563 // Do not search with the iproc processor , that was done before
18564 // above because we are sending info to that processor
18565 if (jproc != iproc)
18566 {
18567 // Get the number of shared boundaries with the jproc processor
18568 const unsigned n_jshd_bnd = this->nshared_boundaries(my_rank, jproc);
18569 // Loop over the shared boundaries
18570 for (unsigned bb = 0; bb < n_jshd_bnd; bb++)
18571 {
18572 // Get the boundary id
18573 const unsigned j_shd_bnd =
18574 this->shared_boundaries_ids(my_rank, jproc, bb);
18575 // Is the node part of this boundary?
18576 if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt))
18577 {
18578 // DEBP("Sending to");
18579 // DEBP(iproc);
18580 // DEBP("Pair of procs where other shared");
18581 // DEBP(my_rank);
18582 // DEBP(jproc);
18583 // DEBP(i_bnd);
18584 node_on_shared_boundary_with_other_processors = true;
18585 // Increase the counter for the number of shared boundaries
18586 // with other processors the node is on
18587 nshared_boundaries_with_other_processors_have_node++;
18588 } // if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt)
18589
18590 } // for (bb<n_jshd_bnd)
18591
18592 } // if (jproc != iproc)
18593
18594 } // for (jproc < nproc)
18595
18596 // If the node is on a shared boundary with another processor
18597 // (my_rank, jproc), then send the flag and look for the info.
18598 if (node_on_shared_boundary_with_other_processors)
18599 {
18600 Flat_packed_unsigneds.push_back(4);
18601#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18603 "Node is on shared boundary no related with the received processor: 4");
18604#endif
18605
18606 // The number of packages of information that will be sent to the
18607 // "iproc" processor. This helps to know how many packages of data
18608 // read from the received processor
18609 Flat_packed_unsigneds.push_back(
18610 nshared_boundaries_with_other_processors_have_node);
18611#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18612 std::stringstream junk;
18613 junk << "Number of other shared boundaries that the node is on: "
18614 << nshared_boundaries_with_other_processors_have_node;
18615 Flat_packed_unsigneds_string.push_back(junk.str());
18616#endif
18617
18618 // Counter to ensure that the correct number of data has been sent
18619 unsigned counter_shd_bnd_with_other_procs_have_node = 0;
18620 // Loop over the shared boundaries with other processors and get:
18621 // 1) The processors defining the shared boundary
18622 // 2) The shared boundary id
18623 // 3) The index of the node on the shared boundary
18624 Vector<unsigned> other_processor_1;
18625 Vector<unsigned> other_processor_2;
18626 Vector<unsigned> shd_bnd_ids;
18627 Vector<unsigned> indexes;
18628 // Loop over the processors again
18629 for (unsigned jproc = 0; jproc < nproc; jproc++)
18630 {
18631 // Do not search with the iproc processor, that was done before
18632 // above
18633 if (jproc != iproc)
18634 {
18635 // Get the number of shared boundaries with the jproc
18636 // processor
18637 const unsigned n_jshd_bnd = this->nshared_boundaries(my_rank, jproc);
18638 for (unsigned bb = 0; bb < n_jshd_bnd; bb++)
18639 {
18640 // Get the boundary id
18641 const unsigned j_shd_bnd =
18642 this->shared_boundaries_ids(my_rank, jproc, bb);
18643 // Is the node part of this boundary?
18644 if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt))
18645 {
18646 // Include the first processor
18647 other_processor_1.push_back(my_rank);
18648 // Include the second processor
18649 other_processor_2.push_back(jproc);
18650 // Include the shared boundary id
18651 shd_bnd_ids.push_back(j_shd_bnd);
18652 // Increase the counter for found shared boundaries with
18653 // other processors
18654 counter_shd_bnd_with_other_procs_have_node++;
18655 }
18656
18657 } // for (bb < nshared_bnd)
18658
18659 } // if (jproc != iproc)
18660
18661 } // for (jproc < nproc)
18662
18663 // Get the indexes of the node on all the shared boundaries where
18664 // it was found
18665 const unsigned n_other_processors = other_processor_1.size();
18666 // Loop over the processors where the node was found
18667 for (unsigned i = 0; i < n_other_processors; i++)
18668 {
18669 // Get the shared boundary id
18670 unsigned shd_bnd_id = shd_bnd_ids[i];
18671 // Get the number of nodes on that shared boundary
18672 const unsigned n_nodes_on_shd_bnd =
18673 nsorted_shared_boundary_node(shd_bnd_id);
18674
18675#ifdef PARANOID
18676 bool found_index_node_on_shared_boundary = false;
18677#endif
18678 for (unsigned i = 0; i < n_nodes_on_shd_bnd; i++)
18679 {
18680 // Get the i-th shared boundary node
18681 Node* shared_node_pt = sorted_shared_boundary_node_pt(shd_bnd_id, i);
18682 // Is the same node?
18683 if (shared_node_pt == nod_pt)
18684 {
18685 // DEBP(i_node);
18686 // DEBP(nod_pt->x(0));
18687 // DEBP(nod_pt->x(1));
18688 // Include the index of the node
18689 indexes.push_back(i);
18690#ifdef PARANOID
18691 // Mark as found the node
18692 found_index_node_on_shared_boundary = true;
18693#endif
18694 break;
18695 } // if (shared_node_pt == nod_pt)
18696
18697 } // for (i < n_nodes_on_shd_bnd)
18698
18699#ifdef PARANOID
18700 if (!found_index_node_on_shared_boundary)
18701 {
18702 std::ostringstream error_message;
18703 error_message << "The index of the node on boundary (" << shd_bnd_id
18704 << "), shared by other processors\nwas not found.\n"
18705 << "The node coordinates are (" << nod_pt->x(0) << ","
18706 << nod_pt->x(1) << ").\n";
18707 throw OomphLibError(
18708 error_message.str(),
18709 "RefineableTriangleMesh::get_required_nodal_information_helper()",
18710 OOMPH_EXCEPTION_LOCATION);
18711 }
18712#endif
18713 } // for (i < n_other_processors)
18714
18715 // Now send the info. but first check that the number of found
18716 // nodes be the same that the previously found shared boundaries
18717 // with the node
18718#ifdef PARANOID
18719 if (counter_shd_bnd_with_other_procs_have_node !=
18720 nshared_boundaries_with_other_processors_have_node)
18721 {
18722 std::ostringstream error_message;
18723 error_message << "The number of shared boundaries where the node is on "
18724 << "is different:\n"
18725 << "nshared_boundaries_with_other_processors_have_node: ("
18726 << nshared_boundaries_with_other_processors_have_node
18727 << ")\n"
18728 << "counter_shd_bnd_with_other_procs_have_node: ("
18729 << counter_shd_bnd_with_other_procs_have_node << ")\n";
18730 throw OomphLibError(
18731 error_message.str(),
18732 "RefineableTriangleMesh::get_required_nodal_information_helper()",
18733 OOMPH_EXCEPTION_LOCATION);
18734 } // if (counter_shd_bnd_with_other_procs_have_node !=
18735 // nshared_boundaries_with_other_processors_have_node)
18736#endif
18737
18738 // Loop over the info. to send it
18739 for (unsigned i = 0; i < n_other_processors; i++)
18740 {
18741 Flat_packed_unsigneds.push_back(other_processor_1[i]);
18742#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18743 std::stringstream junk1;
18744 junk1 << "Processor where the other shared boundary "
18745 << "has the node: " << other_processor_1[i];
18746 Flat_packed_unsigneds_string.push_back(junk1.str());
18747#endif
18748
18749 Flat_packed_unsigneds.push_back(other_processor_2[i]);
18750#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18751 std::stringstream junk2;
18752 junk2 << "Processor where the other shared boundary "
18753 << "has the node: " << other_processor_2[i];
18754 Flat_packed_unsigneds_string.push_back(junk2.str());
18755#endif
18756
18757 Flat_packed_unsigneds.push_back(shd_bnd_ids[i]);
18758#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18759 std::stringstream junk3;
18760 junk3 << "Other shared boundary id where the node is on"
18761 << boundaries[i];
18762 Flat_packed_unsigneds_string.push_back(junk3.str());
18763#endif
18764
18765 Flat_packed_unsigneds.push_back(indexes[i]);
18766#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18767 std::stringstream junk4;
18768 junk4 << "Node index on other shared boundary " << boundaries[i]
18769 << " is " << indexes[i];
18770 Flat_packed_unsigneds_string.push_back(junk4.str());
18771#endif
18772
18773 } // for (i < n_other_processors)
18774
18775 } // if (node_on_shared_boundary_with_other_processors)
18776 else
18777 {
18778 Flat_packed_unsigneds.push_back(0);
18779#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18781 "Node is on any shared boundary with other processors");
18782#endif
18783 } // else if (node_on_shared_boundary_with_other_processors)
18784
18785 // Now check if it is required to send the info. of the node. If the
18786 // node is not on a shared boundary with the iproc processor then we
18787 // need to send the info.
18788
18789 if (!node_on_shared_boundary)
18790 {
18791 // Send all the info. to create it
18792
18793 // Is the Node algebraic? If so, send its ref values and
18794 // an indication of its geometric objects if they are stored
18795 // in the algebraic mesh
18796 AlgebraicNode* alg_nod_pt = dynamic_cast<AlgebraicNode*>(nod_pt);
18797 if (alg_nod_pt != 0)
18798 {
18799 // The external mesh should be algebraic
18800 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(this);
18801
18802 // Get default node update function ID
18803 unsigned update_id = alg_nod_pt->node_update_fct_id();
18804 Flat_packed_unsigneds.push_back(update_id);
18805#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18806 Flat_packed_unsigneds_string.push_back("Alg Node update id");
18807#endif
18808
18809 // Get reference values at default...
18810 unsigned n_ref_val = alg_nod_pt->nref_value();
18811 Flat_packed_unsigneds.push_back(n_ref_val);
18812#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18813 Flat_packed_unsigneds_string.push_back("Alg Node n ref values");
18814#endif
18815 for (unsigned i_ref_val = 0; i_ref_val < n_ref_val; i_ref_val++)
18816 {
18817 Flat_packed_doubles.push_back(alg_nod_pt->ref_value(i_ref_val));
18818 }
18819
18820 // Access geometric objects at default...
18821 unsigned n_geom_obj = alg_nod_pt->ngeom_object();
18822 Flat_packed_unsigneds.push_back(n_geom_obj);
18823#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18824 Flat_packed_unsigneds_string.push_back("Alg Node n geom objects");
18825#endif
18826 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
18827 {
18828 GeomObject* geom_obj_pt = alg_nod_pt->geom_object_pt(i_geom);
18829
18830 // Check this against the stored geometric objects in mesh
18831 unsigned n_geom_list = alg_mesh_pt->ngeom_object_list_pt();
18832
18833 // Default found index to zero
18834 unsigned found_geom_object = 0;
18835 for (unsigned i_list = 0; i_list < n_geom_list; i_list++)
18836 {
18837 if (geom_obj_pt == alg_mesh_pt->geom_object_list_pt(i_list))
18838 {
18839 found_geom_object = i_list;
18840 }
18841 }
18842 Flat_packed_unsigneds.push_back(found_geom_object);
18843#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18844 Flat_packed_unsigneds_string.push_back("Found geom object");
18845#endif
18846 }
18847 } // (if alg_nod_pt!=0)
18848
18849 // Is it a SolidNode?
18850 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(nod_pt);
18851 if (solid_nod_pt != 0)
18852 {
18853 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
18854 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
18855 {
18856 for (unsigned t = 0; t < n_prev; t++)
18857 {
18858 Flat_packed_doubles.push_back(
18859 solid_nod_pt->variable_position_pt()->value(t, i_val));
18860 }
18861 }
18862
18863 Vector<double> values_solid_node;
18864 solid_nod_pt->add_values_to_vector(values_solid_node);
18865 const unsigned nvalues_solid_node = values_solid_node.size();
18866 Flat_packed_unsigneds.push_back(nvalues_solid_node);
18867#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18868 std::stringstream junk;
18869 junk << "Number of values solid node: " << nvalues_solid_node;
18870 Flat_packed_unsigneds_string.push_back(junk.str());
18871#endif
18872 for (unsigned i = 0; i < nvalues_solid_node; i++)
18873 {
18874 Flat_packed_doubles.push_back(values_solid_node[i]);
18875 }
18876 }
18877
18878 // Finally copy info required for all node types
18879 for (unsigned i_val = 0; i_val < n_val; i_val++)
18880 {
18881 for (unsigned t = 0; t < n_prev; t++)
18882 {
18883 Flat_packed_doubles.push_back(nod_pt->value(t, i_val));
18884 }
18885 }
18886
18887 // Now do positions
18888 for (unsigned idim = 0; idim < n_dim; idim++)
18889 {
18890 for (unsigned t = 0; t < n_prev; t++)
18891 {
18892 Flat_packed_doubles.push_back(nod_pt->x(t, idim));
18893 }
18894 }
18895
18896 } // if (!node_on_shared_boundary)
18897 }
18898
18899 //==========start of add_haloed_node_helper===============================
18900 /// Helper to add external haloed node that is not a master
18901 //========================================================================
18902 template<class ELEMENT>
18904 Node* nod_pt)
18905 {
18906 // Attempt to add this node as a haloed node
18907 const unsigned n_haloed_nod = this->nhaloed_node(iproc);
18908 const unsigned haloed_node_index =
18909 this->try_to_add_haloed_node_pt(iproc, nod_pt);
18910
18911 // If it was added then the new index should match the size of the storage
18912 if (haloed_node_index == n_haloed_nod)
18913 {
18914 Flat_packed_unsigneds.push_back(1);
18915
18916#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18917 std::stringstream junk;
18918 junk << "Node needs to be constructed [size="
18919 << Flat_packed_unsigneds.size() << "]; last entry: "
18921 Flat_packed_unsigneds_string.push_back(junk.str());
18922#endif
18923
18924 // This helper function gets all the required information for the
18925 // specified node and stores it into MPI-sendable information
18926 // so that a halo copy can be made on the receiving process
18928 }
18929 else // It was already added
18930 {
18931 Flat_packed_unsigneds.push_back(0);
18932#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18933 std::stringstream junk;
18934 junk << "Node was already added [size=" << Flat_packed_unsigneds.size()
18935 << "]; last entry: "
18937
18938 Flat_packed_unsigneds_string.push_back(junk.str());
18939#endif
18940
18941 // This node is already a haloed node, so tell
18942 // the other process its index in the equivalent halo storage
18943 Flat_packed_unsigneds.push_back(haloed_node_index);
18944#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18945 Flat_packed_unsigneds_string.push_back("haloed node index");
18946#endif
18947 }
18948 }
18949
18950 //================= send_and_receive_haloed_info =======================
18951 /// Send the information of the elements that will be created on the other
18952 /// processor
18953 //======================================================================
18954 template<class ELEMENT>
18956 int& send_proc, int& recv_proc)
18957 {
18958 // Get the communicator of the mesh
18959 OomphCommunicator* comm_pt = this->communicator_pt();
18960
18961 // Set MPI info
18962 MPI_Status status;
18963 MPI_Request request;
18964
18965 // Prepare vectors to receive information
18966 Vector<double> received_double_values;
18967 Vector<unsigned> received_unsigned_values;
18968
18969 // Send the double values associated with halo(ed) elements and nodes
18970 //-------------------------------------------------------------------
18971 unsigned send_count_double_values = Flat_packed_doubles.size();
18972 MPI_Isend(&send_count_double_values,
18973 1,
18974 MPI_UNSIGNED,
18975 send_proc,
18976 1,
18977 comm_pt->mpi_comm(),
18978 &request);
18979
18980 int receive_count_double_values = 0;
18981 MPI_Recv(&receive_count_double_values,
18982 1,
18983 MPI_INT,
18984 recv_proc,
18985 1,
18986 comm_pt->mpi_comm(),
18987 &status);
18988 MPI_Wait(&request, MPI_STATUS_IGNORE);
18989
18990 if (send_count_double_values != 0)
18991 {
18992 MPI_Isend(&Flat_packed_doubles[0],
18993 send_count_double_values,
18994 MPI_DOUBLE,
18995 send_proc,
18996 2,
18997 comm_pt->mpi_comm(),
18998 &request);
18999 }
19000 if (receive_count_double_values != 0)
19001 {
19002 received_double_values.resize(receive_count_double_values);
19003 MPI_Recv(&received_double_values[0],
19004 receive_count_double_values,
19005 MPI_DOUBLE,
19006 recv_proc,
19007 2,
19008 comm_pt->mpi_comm(),
19009 &status);
19010 }
19011 if (send_count_double_values != 0)
19012 {
19013 MPI_Wait(&request, MPI_STATUS_IGNORE);
19014 }
19015
19016 // Now send unsigned values associated with halo(ed) elements and nodes
19017 //---------------------------------------------------------------------
19018 unsigned send_count_unsigned_values = Flat_packed_unsigneds.size();
19019#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19020 unsigned send_count_unsigned_string = Flat_packed_unsigneds_string.size();
19021#ifdef PARANOID
19022 if (send_count_unsigned_string != send_count_unsigned_values)
19023 {
19024 std::ostringstream error_message;
19025 error_message << "The number of unsigned values to send to processor ("
19026 << send_proc
19027 << ") is different from the\nnumber of annotated strings "
19028 << "for the communication\n\n";
19029 throw OomphLibError(
19030 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
19031 }
19032#endif // #ifdef PARANOID
19033#endif
19034 MPI_Isend(&send_count_unsigned_values,
19035 1,
19036 MPI_UNSIGNED,
19037 send_proc,
19038 14,
19039 comm_pt->mpi_comm(),
19040 &request);
19041
19042 int receive_count_unsigned_values = 0;
19043 MPI_Recv(&receive_count_unsigned_values,
19044 1,
19045 MPI_INT,
19046 recv_proc,
19047 14,
19048 comm_pt->mpi_comm(),
19049 &status);
19050
19051 MPI_Wait(&request, MPI_STATUS_IGNORE);
19052
19053 if (send_count_unsigned_values != 0)
19054 {
19055 MPI_Isend(&Flat_packed_unsigneds[0],
19056 send_count_unsigned_values,
19057 MPI_UNSIGNED,
19058 send_proc,
19059 15,
19060 comm_pt->mpi_comm(),
19061 &request);
19062#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19063 for (unsigned i = 0; i < send_count_unsigned_values; i++)
19064 {
19065 oomph_info << "Sent:" << i << " to orig_proc:" << send_proc << " "
19067 << Flat_packed_unsigneds[i] << std::endl;
19068 }
19069#endif
19070 }
19071 if (receive_count_unsigned_values != 0)
19072 {
19073 received_unsigned_values.resize(receive_count_unsigned_values);
19074 MPI_Recv(&received_unsigned_values[0],
19075 receive_count_unsigned_values,
19076 MPI_UNSIGNED,
19077 recv_proc,
19078 15,
19079 comm_pt->mpi_comm(),
19080 &status);
19081 }
19082
19083 if (send_count_unsigned_values != 0)
19084 {
19085 MPI_Wait(&request, MPI_STATUS_IGNORE);
19086 }
19087
19088 // Copy across into original containers -- these can now
19089 //------------------------------------------------------
19090 // be processed by create_external_halo_elements() to generate
19091 //------------------------------------------------------------
19092 // external halo elements
19093 //------------------------
19094 Flat_packed_doubles.resize(receive_count_double_values);
19095 for (int ii = 0; ii < receive_count_double_values; ii++)
19096 {
19097 Flat_packed_doubles[ii] = received_double_values[ii];
19098 }
19099 Flat_packed_unsigneds.resize(receive_count_unsigned_values);
19100 for (int ii = 0; ii < receive_count_unsigned_values; ii++)
19101 {
19102 Flat_packed_unsigneds[ii] = received_unsigned_values[ii];
19103 }
19104 }
19105
19106 //=====================================================================
19107 /// Creates (halo) element on the loop process based on the
19108 /// information received from each processor
19109 //=====================================================================
19110 template<class ELEMENT>
19112 unsigned& iproc,
19113 Vector<Node*>& new_nodes_on_domain,
19114 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
19115 other_proc_shd_bnd_node_pt,
19116 Vector<Vector<Vector<unsigned>>>& global_node_names,
19117 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
19118 Vector<Node*>& global_shared_node_pt)
19119 {
19120#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19122 << " Bool: New element needs to be constructed "
19124 << std::endl;
19125#endif
19126
19128 {
19129 // Create a new element from the communicated values
19130 // and coords from the process that located zeta
19131 GeneralisedElement* new_el_pt = new ELEMENT;
19132
19133 // Add the element, it is a new element in the mesh
19134 this->add_element_pt(new_el_pt);
19135
19136 // Add halo element to this mesh
19137 this->add_root_halo_element_pt(iproc, new_el_pt);
19138
19139 // Cast to the FE pointer
19140 FiniteElement* f_el_pt = dynamic_cast<FiniteElement*>(new_el_pt);
19141
19142 // Check if new element is associated to any boundary
19143 this->add_halo_element_helper(iproc, f_el_pt);
19144
19145 // Now we add nodes to the new element
19146 unsigned n_node = f_el_pt->nnode();
19147
19148 for (unsigned j = 0; j < n_node; j++)
19149 {
19150 Node* new_nod_pt = 0;
19151
19152 // Call the add halo node helper function
19153 add_halo_node_helper(new_nod_pt,
19154 new_nodes_on_domain,
19155 other_proc_shd_bnd_node_pt,
19156 iproc,
19157 j,
19158 f_el_pt,
19159 global_node_names,
19160 node_name_to_global_index,
19161 global_shared_node_pt);
19162
19163 } // for (j<n_nod)
19164 }
19165 else // the element already exists as halo
19166 {
19167#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19169 << " Index of existing halo element "
19171 << std::endl;
19172#endif
19173 // The index itself is in Flat_packed_unsigneds[...]
19174 unsigned halo_ele_index =
19176
19177 // Use this index to get the element
19178 FiniteElement* f_el_pt = dynamic_cast<FiniteElement*>(
19179 this->root_halo_element_pt(iproc, halo_ele_index));
19180
19181 // If it's not a finite element die
19182 if (f_el_pt == 0)
19183 {
19184 throw OomphLibError("Halo element is not a FiniteElement\n",
19185 OOMPH_CURRENT_FUNCTION,
19186 OOMPH_EXCEPTION_LOCATION);
19187 }
19188
19189 } // else the element already exists as halo
19190 }
19191
19192 //========start of add_halo_element_helper==============================
19193 /// Helper function to create (halo) elements on the loop
19194 /// process based on the info received in send_and_received_located_info
19195 /// This function is in charge of verify if the element is associated to
19196 /// a boundary
19197 //======================================================================
19198 template<class ELEMENT>
19200 unsigned& iproc, FiniteElement* ele_pt)
19201 {
19202#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19204 << " Bool: Element is associated to an original boundary "
19206 << std::endl;
19207#endif
19208
19210 {
19211#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19213 << " How many boundaries are associated with the element "
19215 << std::endl;
19216#endif
19217 const unsigned nassociated_boundaries =
19219
19220 for (unsigned b = 0; b < nassociated_boundaries; b++)
19221 {
19222#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19224 << " Boundary associated to the element "
19226 << std::endl;
19227#endif
19228 const unsigned bnd =
19230
19231#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19233 << " Face index of the element "
19235 << std::endl;
19236#endif
19237 const unsigned face_index =
19239
19240 // Associate the element with the boundary and establish as many
19241 // face indexes it has
19242 this->Boundary_element_pt[bnd].push_back(ele_pt);
19243 this->Face_index_at_boundary[bnd].push_back(face_index);
19244
19245 } // (b < nassociated_boundaries)
19246
19247 // Here read the info. regarding the boundary-region of the element
19248#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19250 << " Bool: Element is associated to a boundary-region "
19252 << std::endl;
19253#endif
19254
19256 {
19257#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19260 << " How many boundaries-regions are associated with the element "
19262 << std::endl;
19263#endif
19264 const unsigned nassociated_boundaries_and_regions =
19266
19267 for (unsigned br = 0; br < nassociated_boundaries_and_regions; br++)
19268 {
19269#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19271 << " Boundary associated to the element "
19273 << std::endl;
19274#endif
19275 const unsigned bnd =
19277
19278#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19280 << " Region associated to the element "
19282 << std::endl;
19283#endif
19284 const unsigned region =
19286
19287#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19289 << " Face index of the element in boundary-region "
19291 << std::endl;
19292#endif
19293 const unsigned face_index =
19295
19296 // Associate the element with the boundary-regions and establish
19297 // as many face indexes it has
19298 this->Boundary_region_element_pt[bnd][region].push_back(ele_pt);
19299 this->Face_index_region_at_boundary[bnd][region].push_back(
19300 face_index);
19301
19302 } // for (br < nassociated_boundaries_and_regions)
19303
19304 } // Is the element associated with a boundary-region?
19305 }
19306
19307 // Now check if the element is associated to a shared boundary
19308#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19310 << " Bool: Element is associated to a shared boundary "
19312 << std::endl;
19313#endif
19315 {
19316#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19319 << " How many shared boundaries are associated with the element "
19321 << std::endl;
19322#endif
19323 const unsigned nassociated_shared_boundaries =
19325
19326 for (unsigned b = 0; b < nassociated_shared_boundaries; b++)
19327 {
19328#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19330 << " Shared boundary associated to the element "
19332 << std::endl;
19333#endif
19334 const unsigned bnd =
19336
19337#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19340 << " Face index of the element associated to the shared boundary "
19342 << std::endl;
19343#endif
19344
19345 const unsigned face_index =
19347
19348 this->add_shared_boundary_element(bnd, ele_pt);
19349 this->add_face_index_at_shared_boundary(bnd, face_index);
19350
19351 } // (b < nassociated_shared_boundaries)
19352
19353 } // The element is associted with a shared boundary
19354 }
19355
19356 //========start of add_halo_node_helper==========================
19357 /// Helper function to add halo node
19358 //===============================================================
19359 template<class ELEMENT>
19361 Node*& new_nod_pt,
19362 Vector<Node*>& new_nodes_on_domain,
19363 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
19364 other_proc_shd_bnd_node_pt,
19365 unsigned& iproc,
19366 unsigned& node_index,
19367 FiniteElement* const& new_el_pt,
19368 Vector<Vector<Vector<unsigned>>>& global_node_names,
19369 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
19370 Vector<Node*>& global_shared_node_pt)
19371 {
19372 // Given the node, received information about them from process
19373 // iproc, construct them on the current process
19374#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19376 << " Bool: New node needs to be constructed "
19378 << std::endl;
19379#endif
19381 {
19382 // Construct a new node based upon sent information, or copy a node
19383 // from one of the shared boundaries
19384 construct_new_halo_node_helper(new_nod_pt,
19385 new_nodes_on_domain,
19386 other_proc_shd_bnd_node_pt,
19387 iproc,
19388 node_index,
19389 new_el_pt,
19390 global_node_names,
19391 node_name_to_global_index,
19392 global_shared_node_pt);
19393 }
19394 else
19395 {
19396#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19398 << " Index of existing halo node "
19400 << std::endl;
19401#endif
19402
19403 // Copy node from received location
19404 new_nod_pt = new_nodes_on_domain
19406
19407 new_el_pt->node_pt(node_index) = new_nod_pt;
19408 }
19409 }
19410
19411 //========start of construct_new_halo_node_helper=================
19412 // Helper function which constructs a new external halo node (on new element)
19413 // with the required information sent from the haloed process
19414 //========================================================================
19415 template<class ELEMENT>
19417 Node*& new_nod_pt,
19418 Vector<Node*>& new_nodes_on_domain,
19419 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
19420 other_proc_shd_bnd_node_pt,
19421 unsigned& iproc,
19422 unsigned& node_index,
19423 FiniteElement* const& new_el_pt,
19424 Vector<Vector<Vector<unsigned>>>& global_node_names,
19425 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
19426 Vector<Node*>& global_shared_node_pt)
19427 {
19428 // The first entry indicates the number of values at this new Node
19429 //(which may be different across the same element e.g. Lagrange multipliers)
19430#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19432 << " Number of values of external halo node "
19434 << std::endl;
19435#endif
19437
19438 // Null TimeStepper for now
19439 TimeStepper* time_stepper_pt = this->Time_stepper_pt;
19440 // Default number of previous values to 1
19441 unsigned n_prev = time_stepper_pt->ntstorage();
19442
19443 // ------------------------------------------------------
19444 // Check if the node is on an original boundary
19445#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19447 << " Is the node on an original boundary "
19449 << std::endl;
19450#endif
19451
19452 // Flag to indicate if the node is on original boundaries
19453 const unsigned node_on_original_boundaries =
19455
19456 // Store the original boundaries where the node is on
19457 Vector<unsigned> original_boundaries_node_is_on;
19458 // Store the zeta coordinates of the node on the original boundaries
19459 Vector<double> zeta_coordinates;
19460 // Store the number of original boundaries the node is on
19461 unsigned n_original_boundaries_node_is_on = 0;
19462
19463 if (node_on_original_boundaries == 2)
19464 {
19465 // How many original boundaries does the node live on?
19466#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19468 << " Number of boundaries the node is on: "
19470 << std::endl;
19471#endif
19472 n_original_boundaries_node_is_on =
19474
19475 // Resize the containers
19476 original_boundaries_node_is_on.resize(n_original_boundaries_node_is_on);
19477 zeta_coordinates.resize(n_original_boundaries_node_is_on);
19478
19479 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
19480 {
19481 // Boundary number
19482#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19484 << " Node is on boundary "
19486 << std::endl;
19487#endif
19488 original_boundaries_node_is_on[i] =
19490 zeta_coordinates[i] =
19492 }
19493
19494 } // if (node_on_original_boundaries==2)
19495#ifdef PARANOID
19496 else
19497 {
19498 if (node_on_original_boundaries != 0)
19499 {
19500 std::ostringstream error_message;
19501 error_message
19502 << "The current node is not on an original boundary, this should\n"
19503 << "be indicated by a zero flag. However, the read value for\n"
19504 << "that flag is (" << node_on_original_boundaries << ").\n\n";
19505 throw OomphLibError(
19506 error_message.str(),
19507 "RefineableTriangleMesh::construct_new_halo_node_helper()",
19508 OOMPH_EXCEPTION_LOCATION);
19509 } // if (node_on_original_boundaries != 0)
19510 }
19511#endif
19512
19513 // --------------------------------------------------------------
19514 // Check if the node was on a shared boundary with the iproc
19515 // processor
19516#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19518 << " Is node on shared boundary? "
19520 << std::endl;
19521#endif
19522 const unsigned is_node_on_shared_boundary =
19524 if (is_node_on_shared_boundary == 1)
19525 {
19526 // How many shared boundaries does the node live on?
19527#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19529 << " Number of boundaries the node is on: "
19531 << std::endl;
19532#endif
19533 const unsigned n_shd_bnd_node_is_on =
19535 Vector<unsigned> shd_bnds_node_is_on(n_shd_bnd_node_is_on);
19536 for (unsigned i = 0; i < n_shd_bnd_node_is_on; i++)
19537 {
19538 // Shared boundary number
19539#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19541 << " Node is on boundary "
19543 << std::endl;
19544#endif
19545 shd_bnds_node_is_on[i] =
19547 }
19548
19549 // Get the index of the node on the shared boundary
19550#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19552 << " Index of node on boundary "
19554 << std::endl;
19555#endif
19556 // Get the node index of the node on the shared boundary
19557 unsigned node_index_on_shared_boundary =
19559
19560 // Get the pointer to the node with the received info.
19561 new_nod_pt = this->sorted_shared_boundary_node_pt(
19562 shd_bnds_node_is_on[0], node_index_on_shared_boundary);
19563
19564 } // if (is_node_on_shared_boundary == 1)
19565#ifdef PARANOID
19566 else
19567 {
19568 if (is_node_on_shared_boundary != 0)
19569 {
19570 std::ostringstream error_message;
19571 error_message
19572 << "The current node is not on a shared boundary, this should\n"
19573 << "be indicated by a zero flag. However, the read value for\n"
19574 << "that flag is (" << is_node_on_shared_boundary << ").\n\n";
19575 throw OomphLibError(
19576 error_message.str(),
19577 "RefineableTriangleMesh::construct_new_halo_node_helper()",
19578 OOMPH_EXCEPTION_LOCATION);
19579 } // if (node_on_shared_boundary != 0)
19580 }
19581#endif
19582
19583 // ------------------------------------------------------------
19584 // Is the node on a shared boundary with other processor?
19585#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19587 << " Is the node on shared boundaries with other processors "
19589 << std::endl;
19590#endif
19591
19592 // Is the node in shared boundaries no associated with the
19593 // receiver processor
19594 const unsigned is_the_node_in_shared_boundaries_with_other_processors =
19596
19597 // The containers where to store the info.
19598 Vector<unsigned> other_processor_1;
19599 Vector<unsigned> other_processor_2;
19600 Vector<unsigned> other_shared_boundaries;
19601 Vector<unsigned> other_indexes;
19602
19603 // How many shared bounaries with other processors the node lives on
19604 unsigned n_shd_bnd_with_other_procs_have_node = 0;
19605
19606 // Is the node on shared boundaries with other processors
19607 if (is_the_node_in_shared_boundaries_with_other_processors == 4)
19608 {
19609#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19611 << " In how many shared boundaries with other "
19612 << "processors is the node "
19614 << std::endl;
19615#endif
19616
19617 // How many nodes on other shared boundaries were found
19618 n_shd_bnd_with_other_procs_have_node =
19620
19621 // Resize the containers
19622 other_processor_1.resize(n_shd_bnd_with_other_procs_have_node);
19623 other_processor_2.resize(n_shd_bnd_with_other_procs_have_node);
19624 other_shared_boundaries.resize(n_shd_bnd_with_other_procs_have_node);
19625 other_indexes.resize(n_shd_bnd_with_other_procs_have_node);
19626
19627 for (unsigned i = 0; i < n_shd_bnd_with_other_procs_have_node; i++)
19628 {
19629#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19631 << " Processor where the other shared boundary"
19632 << "has the node"
19634 << std::endl;
19635#endif
19636 // Read the other processor 1
19637 other_processor_1[i] =
19639
19640#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19642 << " Processor where the other shared boundary"
19643 << "has the node"
19645 << std::endl;
19646#endif
19647 // Read the other processor 2
19648 other_processor_2[i] =
19650
19651#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19653 << " Other shared boundary id where the node is on: "
19655 << std::endl;
19656#endif
19657
19658 // Read the other shared boundary id
19659 other_shared_boundaries[i] =
19661
19662#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19664 << " Node index on the other shared boundary "
19666 << std::endl;
19667#endif
19668
19669 // Read the node index on the other shared boundary
19670 other_indexes[i] =
19672
19673 } // for (i < n_shd_bnd_with_other_procs_have_node)
19674
19675 } // if (is_the_node_in_shared_boundaries_with_other_processors == 4)
19676#ifdef PARANOID
19677 else
19678 {
19679 if (is_the_node_in_shared_boundaries_with_other_processors != 0)
19680 {
19681 std::ostringstream error_message;
19682 error_message
19683 << "The current node is not on a shared boundary with\n"
19684 << "other processors, this should be indicated by a zero flag.\n"
19685 << "However, the read value for that flag is ("
19686 << is_the_node_in_shared_boundaries_with_other_processors << ").\n\n";
19687 throw OomphLibError(
19688 error_message.str(),
19689 "RefineableTriangleMesh::construct_new_halo_node_helper()",
19690 OOMPH_EXCEPTION_LOCATION);
19691 }
19692 }
19693#endif
19694
19695 // Now we have all the info. to decide whether the node should be
19696 // created or not
19697
19698 // First check if the node is a shared boundary node
19699 if (is_node_on_shared_boundary == 1)
19700 {
19701 // We already have the node, we do not need to create it
19702
19703 // Only check if we need to add boundary info. to the node
19704 if (node_on_original_boundaries == 2)
19705 {
19706 // The node is a boundary node, add the boundary info. before
19707 // adding it to the domain
19708
19709 // Associate the node to the given boundaries
19710 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
19711 {
19712 add_boundary_node(original_boundaries_node_is_on[i], new_nod_pt);
19713 // Establish the boundary coordinates for the node
19714 Vector<double> zeta(1);
19715 zeta[0] = zeta_coordinates[i];
19716 new_nod_pt->set_coordinates_on_boundary(
19717 original_boundaries_node_is_on[i], zeta);
19718 }
19719
19720 } // if (node_on_original_boundaries==2)
19721
19722 // Add the node to the domain
19723 new_nodes_on_domain.push_back(new_nod_pt);
19724
19725 // Add the node to the element
19726 new_el_pt->node_pt(node_index) = new_nod_pt;
19727
19728 } // if (is_node_on_shared_boundary == 1)
19729
19730 // Now check if the node is on a shared boundary with another
19731 // processor, if that is the case try to find the node that may have
19732 // been already sent by the other processors
19733
19734 // This flags indicates if the node was found, and then decide if it
19735 // is required to create the node
19736 bool found_node_in_other_shared_boundaries = false;
19737 // Flag to indicate whether the node should be created as a boundary
19738 // node or not. If the node lies on a shared boundary with other
19739 // processor the we create it as a boundary node. The processor from
19740 // which we are receiving info. (iproc) may not know that the node
19741 // lies on an original boundary. If the node lies on an original
19742 // boundary then its info. will be sent by another processor, then
19743 // we can set its boundary info. since the node was constructed as a
19744 // boundary node
19745 bool build_node_as_boundary_node = false;
19746
19747 if (is_the_node_in_shared_boundaries_with_other_processors == 4)
19748 {
19749 // Build the node as a boundary node
19750 build_node_as_boundary_node = true;
19751
19752 // Try to get the node pointer in case that the node has been
19753 // already sent by the other processors
19754
19755 // Get the number of initial shared boundaries to correct the
19756 // index of the shared boundary
19757 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
19758
19759 // Add the found nodes in the container
19760 Vector<Node*> found_node_pt;
19761
19762 // Now try to find the node in any of the other shared boundaries
19763 for (unsigned i = 0; i < n_shd_bnd_with_other_procs_have_node; i++)
19764 {
19765 // We always check with the lower processor number. The
19766 // info. is only stored in one direction. More importantly,
19767 // this is done with the hope that the info. has been already
19768 // received from the other processor given that its info. was
19769 // processed before the current processor (iproc). NOTE that
19770 // it is not always the case that this info. has been received
19771 // from the other processors since it may have not require to
19772 // send the elements (and nodes) on the shared boundary with
19773 // the current processor (iproc).
19774 unsigned oproc1 = other_processor_1[i];
19775 unsigned oproc2 = other_processor_2[i];
19776 if (other_processor_1[i] > other_processor_2[i])
19777 {
19778 oproc1 = other_processor_2[i];
19779 oproc2 = other_processor_1[i];
19780 } // if (other_processor_1[i] > other_processor_2[i])
19781
19782 // Re-compute the shared boundary id between the other
19783 // processors
19784 const unsigned shd_bnd_id =
19785 other_shared_boundaries[i] - initial_shd_bnd_id;
19786
19787 // Read the index
19788 const unsigned index = other_indexes[i];
19789
19790 // Check if there are nodes received from the other processor
19791 // and with the given shared boundary
19792 const unsigned n_nodes_on_other_processor =
19793 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].size();
19794
19795 if (n_nodes_on_other_processor > 0)
19796 {
19797 // Check if we can find the index of the node in that
19798 // other processor and shared boundary id
19799 std::map<unsigned, Node*>::iterator it =
19800 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].find(index);
19801
19802 // If the index exist then get the node pointer
19803 if (it !=
19804 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].end())
19805 {
19806 // Mark the node as found
19807 found_node_in_other_shared_boundaries = true;
19808 // Get the node pointer
19809 Node* tmp_node_pt = (*it).second;
19810
19811 // Push back the node pointer
19812 found_node_pt.push_back(tmp_node_pt);
19813
19814 } // if (it!=
19815 // other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].end())
19816
19817 } // if (n_nodes_on_other_processor > 0)
19818
19819 } // for (i < n_shd_bnd_with_other_procs_have_node)
19820
19821 // If the node was found, then all their instances should be the
19822 // same but better check
19823 if (found_node_in_other_shared_boundaries)
19824 {
19825#ifdef PARANOID
19826 const unsigned n_times_node_found = found_node_pt.size();
19827 for (unsigned j = 1; j < n_times_node_found; j++)
19828 {
19829 if (found_node_pt[j - 1] != found_node_pt[j])
19830 {
19831 std::ostringstream error_message;
19832 error_message
19833 << "The instances of the node that was found on\n"
19834 << "shared boundaries with other processors (but not\n"
19835 << "on shared boundaries with this processor) are not\n"
19836 << "the same.\n"
19837 << "These are the coordinates of the instances of the\n"
19838 << "nodes:\n"
19839 << "(" << found_node_pt[j - 1]->x(0) << ", "
19840 << found_node_pt[j - 1]->x(1) << ")\n"
19841 << "(" << found_node_pt[j]->x(0) << ", " << found_node_pt[j]->x(1)
19842 << ")\n"
19843 << "Dont be surprised if they are the same since the "
19844 << "node is\nrepeated.\n";
19845 throw OomphLibError(error_message.str(),
19846 OOMPH_CURRENT_FUNCTION,
19847 OOMPH_EXCEPTION_LOCATION);
19848
19849 } // if (found_node_pt[j-1] != found_node_pt[j])
19850
19851 } // for (j < ntimes_node_found)
19852#endif // #ifdef PARANOID
19853
19854 // Check if the node is a shared boundary node from the
19855 // current processor and the iproc processor, if that is the
19856 // case, and the node is also on a shared boundary with other
19857 // processor, then the pointer should be the same!!!
19858 if (is_node_on_shared_boundary == 1)
19859 {
19860 // const unsigned n_times_node_found = found_node_pt.size();
19861 // The pointer to the node is already assigned, it was
19862 // assigned when the node was found to be on a shared
19863 // boundary with the sending processor (iproc). Check that
19864 // any previous instances of the node have been copied
19865 // from the shared boundary, if that is not the case then
19866 // there is a problem
19867 if (found_node_pt[0] != new_nod_pt)
19868 {
19869 std::ostringstream error_message;
19870 error_message
19871 << "The pointer of the node that was found to be on a\n"
19872 << "shared boundary with other processor(s) and the pointer\n"
19873 << "of the node on shared boundary with the receiver\n"
19874 << "processor (iproc) are not the same. This means we have a\n"
19875 << "repeated node)\n"
19876 << "The coordinates for the nodes are:\n"
19877 << "(" << found_node_pt[0]->x(0) << ", " << found_node_pt[0]->x(1)
19878 << ")\n"
19879 << "(" << new_nod_pt->x(0) << ", " << new_nod_pt->x(1) << ")\n"
19880 << "Dont be surprised if they are the same since the "
19881 << "node is\nrepeated.\n";
19882 throw OomphLibError(error_message.str(),
19883 OOMPH_CURRENT_FUNCTION,
19884 OOMPH_EXCEPTION_LOCATION);
19885
19886 } // if (found_node_pt[i] != new_nod_pt)
19887
19888 } // if (is_node_on_shared_boundary == 1)
19889 else
19890 {
19891 // Take the first instance of the node in case that it was
19892 // found and is not on a shared boundary with the iproc
19893 // processor (the processor from which we are receiving
19894 // the info.)
19895 new_nod_pt = found_node_pt[0];
19896 }
19897
19898 } // if (found_node_in_other_shared_boundaries)
19899
19900 } // if (is_the_node_in_shared_boundaries_with_other_processors == 4)
19901
19902 // -----------------------------------------------------------------
19903 // Create the node or read the received info if the node is not on a
19904 // shared boundary with the iproc processor
19905 if (is_node_on_shared_boundary != 1)
19906 {
19907 // If the node is on a shared boundary with other processor we
19908 // need to read all the info. since the processor that sent the
19909 // info. did not know that the node is part of another shared
19910 // boundary
19911
19912 // If the node is not on a shared boundary (with any processor),
19913 // or if this is the first time that the info. of the node is
19914 // received from any of the processors with which it has a shared
19915 // boundary, then we create the node
19916
19917 // Is the node a boundary node or should it be build as a boundary
19918 // node because it is on a shared boundary with other processors
19919 if (node_on_original_boundaries == 2 || build_node_as_boundary_node)
19920 {
19921 // Check if necessary to create the node, or if it has been
19922 // already found in shared boundaries with other processors
19923 if (!found_node_in_other_shared_boundaries)
19924 {
19925 // Construct a boundary node
19926 if (time_stepper_pt != 0)
19927 {
19928 new_nod_pt =
19929 new_el_pt->construct_boundary_node(node_index, time_stepper_pt);
19930 }
19931 else
19932 {
19933 new_nod_pt = new_el_pt->construct_boundary_node(node_index);
19934 }
19935
19936 } // if (!found_node_in_other_shared_boundaries)
19937 else
19938 {
19939 // If the node was found then assign the node to the element
19940 new_el_pt->node_pt(node_index) = new_nod_pt;
19941
19942 } // else if (!found_node_in_other_shared_boundaries)
19943
19944 // Associate the node to the given boundaries
19945 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
19946 {
19947 add_boundary_node(original_boundaries_node_is_on[i], new_nod_pt);
19948 // Establish the boundary coordinates for the node
19949 Vector<double> zeta(1);
19950 zeta[0] = zeta_coordinates[i];
19951 new_nod_pt->set_coordinates_on_boundary(
19952 original_boundaries_node_is_on[i], zeta);
19953 }
19954
19955 } // if (node is on an original boundary)
19956 else
19957 {
19958 // Check if necessary to create the node, or if it has been
19959 // already found in shared boundaries with other processors
19960 if (!found_node_in_other_shared_boundaries)
19961 {
19962 // Construct an ordinary (non-boundary) node
19963 if (time_stepper_pt != 0)
19964 {
19965 new_nod_pt = new_el_pt->construct_node(node_index, time_stepper_pt);
19966 }
19967 else
19968 {
19969 new_nod_pt = new_el_pt->construct_node(node_index);
19970 }
19971 } // if (!found_node_in_other_shared_boundaries)
19972 else
19973 {
19974 // If the node was found then assign the node to the element
19975 new_el_pt->node_pt(node_index) = new_nod_pt;
19976 } // else if (!found_node_in_other_shared_boundaries)
19977
19978 } // else (the node is not a boundary node)
19979
19980 // ... and gather all its information
19981
19982 // If the node was found or not in other shared boundaries, this
19983 // is the first time the node is received from this processor
19984 // (iproc), therefore it is added to the vector of nodes received
19985 // from this processor (iproc)
19986 new_nodes_on_domain.push_back(new_nod_pt);
19987
19988 // Check if necessary to state all the info. to the node if it has
19989 // been already found in shared boundaries with other processors
19990 if (!found_node_in_other_shared_boundaries)
19991 {
19992 // Add the node to the general node storage
19993 this->add_node_pt(new_nod_pt);
19994 } // if (!found_node_in_other_shared_boundaries)
19995
19996 // Is the new constructed node Algebraic?
19997 AlgebraicNode* new_alg_nod_pt = dynamic_cast<AlgebraicNode*>(new_nod_pt);
19998
19999 // If it is algebraic, its node update functions will
20000 // not yet have been set up properly
20001 if (new_alg_nod_pt != 0)
20002 {
20003 // The AlgebraicMesh is the external mesh
20004 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(this);
20005
20006 /// The first entry of All_alg_nodal_info contains
20007 /// the default node update id
20008 /// e.g. for the quarter circle there are
20009 /// "Upper_left_box", "Lower right box" etc...
20010#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20012 << " Alg node update id "
20014 << std::endl;
20015#endif
20016
20017 unsigned update_id =
20019
20020 Vector<double> ref_value;
20021
20022 // The size of this vector is in the next entry
20023 // of All_alg_nodal_info
20024#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20026 << " Alg node # of ref values "
20028 << std::endl;
20029#endif
20030 unsigned n_ref_val =
20032
20033 // The reference values themselves are in
20034 // All_alg_ref_value
20035 ref_value.resize(n_ref_val);
20036 for (unsigned i_ref = 0; i_ref < n_ref_val; i_ref++)
20037 {
20038 ref_value[i_ref] =
20040 }
20041
20042 Vector<GeomObject*> geom_object_pt;
20043 /// again we need the size of this vector as it varies
20044 /// between meshes; we also need some indication
20045 /// as to which geometric object should be used...
20046
20047 // The size of this vector is in the next entry
20048 // of All_alg_nodal_info
20049#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20051 << " Alg node # of geom objects "
20053 << std::endl;
20054#endif
20055 unsigned n_geom_obj =
20057
20058 // The remaining indices are in the rest of
20059 // All_alg_nodal_info
20060 geom_object_pt.resize(n_geom_obj);
20061 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
20062 {
20063#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20065 << " Alg node: geom object index "
20067 << std::endl;
20068#endif
20069 unsigned geom_index =
20071 // This index indicates which of the AlgebraicMesh's
20072 // stored geometric objects should be used
20073 // (0 is a null pointer; everything else should have
20074 // been filled in by the specific Mesh). If it
20075 // hasn't been filled in then the update_node_update
20076 // call should fix it
20077 geom_object_pt[i_geom] = alg_mesh_pt->geom_object_list_pt(geom_index);
20078 }
20079
20080 // Check if necessary to state all the info. to the node if it has
20081 // been already found in shared boundaries with other processors
20082 if (!found_node_in_other_shared_boundaries)
20083 {
20084 /// For the received update_id, ref_value, geom_object
20085 /// call add_node_update_info
20086 new_alg_nod_pt->add_node_update_info(
20087 update_id, alg_mesh_pt, geom_object_pt, ref_value);
20088
20089 /// Now call update_node_update
20090 alg_mesh_pt->update_node_update(new_alg_nod_pt);
20091
20092 } // if (!found_node_in_other_shared_boundaries)
20093
20094 } // if (new_alg_nod_pt!=0)
20095
20096 // Check if necessary to state all the info. to the node if it has
20097 // been already found in shared boundaries with other processors
20098 if (!found_node_in_other_shared_boundaries)
20099 {
20100 // Is the node a MacroElementNodeUpdateNode?
20101 MacroElementNodeUpdateNode* macro_nod_pt =
20102 dynamic_cast<MacroElementNodeUpdateNode*>(new_nod_pt);
20103
20104 if (macro_nod_pt != 0)
20105 {
20106 // Need to call set_node_update_info; this requires
20107 // a Vector<GeomObject*> (taken from the mesh)
20108 Vector<GeomObject*> geom_object_vector_pt;
20109
20110 // Access the required geom objects from the
20111 // MacroElementNodeUpdateMesh
20112 MacroElementNodeUpdateMesh* macro_mesh_pt =
20113 dynamic_cast<MacroElementNodeUpdateMesh*>(this);
20114 geom_object_vector_pt = macro_mesh_pt->geom_object_vector_pt();
20115
20116 // Get local coordinate of node in new element
20117 Vector<double> s_in_macro_node_update_element;
20118 new_el_pt->local_coordinate_of_node(node_index,
20119 s_in_macro_node_update_element);
20120
20121 // Set node update info for this node
20122 macro_nod_pt->set_node_update_info(
20123 new_el_pt, s_in_macro_node_update_element, geom_object_vector_pt);
20124 }
20125
20126 } // if (!found_node_in_other_shared_boundaries)
20127
20128 // If there are additional values, resize the node
20129 unsigned n_new_val = new_nod_pt->nvalue();
20130
20131 // Check if necessary to state all the info. to the node if it has
20132 // been already found in shared boundaries with other processors
20133 if (!found_node_in_other_shared_boundaries)
20134 {
20135 if (n_val > n_new_val)
20136 {
20137 // If it has been necessary to resize then it may be becuse
20138 // the node is on a FSI boundary, if that is the case we need
20139 // to set a map for these external values
20140
20141 // Cast to a boundary node
20142 BoundaryNodeBase* bnod_pt =
20143 dynamic_cast<BoundaryNodeBase*>(new_nod_pt);
20144
20145 // Create storage, if it doesn't already exist, for the map
20146 // that will contain the position of the first entry of
20147 // this face element's additional values,
20149 {
20151 new std::map<unsigned, unsigned>;
20152 }
20153
20154 // Get pointer to the map
20155 std::map<unsigned, unsigned>* map_pt =
20157
20158 // The id of the face to which this node belong in the bulk
20159 // element
20160 const unsigned id_face = 0;
20161 // We only resize the node values Vector if we haven't done it yet
20162 std::map<unsigned, unsigned>::const_iterator p =
20163 map_pt->find(id_face);
20164
20165 // If this node hasn't been resized for current id
20166 if (p == map_pt->end())
20167 {
20168 // assign the face element id and the position of the
20169 // first entry to the boundary node
20170 (*map_pt)[id_face] = n_new_val;
20171
20172 // resize the node vector of values
20173 new_nod_pt->resize(n_val);
20174 }
20175
20176 } // if (n_val>n_new_val)
20177
20178 } // if (!found_node_in_other_shared_boundaries)
20179
20180 // Is the new node a SolidNode?
20181 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(new_nod_pt);
20182 if (solid_nod_pt != 0)
20183 {
20184 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
20185 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
20186 {
20187 for (unsigned t = 0; t < n_prev; t++)
20188 {
20189 double read_data =
20191
20192 // Check if necessary to state all the info. to the node if it has
20193 // been already found in shared boundaries with other processors
20194 if (!found_node_in_other_shared_boundaries)
20195 {
20196 solid_nod_pt->variable_position_pt()->set_value(
20197 t, i_val, read_data);
20198 } // if (!found_node_in_other_shared_boundaries)
20199 }
20200 }
20201
20202#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20204 << " Number of values solid node: "
20206 << std::endl;
20207#endif
20208 const unsigned nvalues_solid_node =
20210 Vector<double> values_solid_node(nvalues_solid_node);
20211 for (unsigned i = 0; i < nvalues_solid_node; i++)
20212 {
20213 values_solid_node[i] =
20215 }
20216
20217 // Check if necessary to state all the info. to the node if it has
20218 // been already found in shared boundaries with other processors
20219 if (!found_node_in_other_shared_boundaries)
20220 {
20221 unsigned index = 0;
20222 solid_nod_pt->read_values_from_vector(values_solid_node, index);
20223 }
20224 }
20225
20226 // Get copied history values
20227 // unsigned n_val=new_nod_pt->nvalue();
20228 for (unsigned i_val = 0; i_val < n_val; i_val++)
20229 {
20230 for (unsigned t = 0; t < n_prev; t++)
20231 {
20232 double read_data =
20234
20235 // Check if necessary to state all the info. to the node if it
20236 // has been already found in shared boundaries with other
20237 // processors
20238 if (!found_node_in_other_shared_boundaries)
20239 {
20240 new_nod_pt->set_value(t, i_val, read_data);
20241 } // if (!found_node_in_other_shared_boundaries)
20242 }
20243 }
20244
20245 // Get copied history values for positions
20246 unsigned n_dim = new_nod_pt->ndim();
20247 for (unsigned idim = 0; idim < n_dim; idim++)
20248 {
20249 for (unsigned t = 0; t < n_prev; t++)
20250 {
20251 double read_data =
20253
20254 // Check if necessary to state all the info. to the node if it
20255 // has been already found in shared boundaries with other
20256 // processors
20257 if (!found_node_in_other_shared_boundaries)
20258 {
20259 // Copy to coordinate
20260 new_nod_pt->x(t, idim) = read_data;
20261
20262 } // if (!found_node_in_other_shared_boundaries)
20263 }
20264 }
20265
20266 } // if (is_node_on_shared_boundary != 1)
20267
20268 // If the node was not found in other shared boundaries (possibly
20269 // because it is the first time the node has been sent) then copy
20270 // the node to the shared boundaries where it should be, use the
20271 // special container for this cases
20272 if (n_shd_bnd_with_other_procs_have_node > 0 && // The node is on
20273 // shared
20274 // boundaries with
20275 // other processors
20276 !found_node_in_other_shared_boundaries) // The node has not
20277 // been previously
20278 // set as
20279 // shared with
20280 // other processors
20281 // (first time)
20282 {
20283 // Update the node pointer in all the (references) of the node
20284 this->update_other_proc_shd_bnd_node_helper(new_nod_pt,
20285 other_proc_shd_bnd_node_pt,
20286 other_processor_1,
20287 other_processor_2,
20288 other_shared_boundaries,
20289 other_indexes,
20290 global_node_names,
20291 node_name_to_global_index,
20292 global_shared_node_pt);
20293
20294 } // if (!found_node_in_other_shared_boundaries)
20295 }
20296
20297 //========start of update_other_proc_shd_bnd_node_helper=================
20298 // Helper function that assigns/updates the references to the node so
20299 // that it can be found with any other reference
20300 //========================================================================
20301 template<class ELEMENT>
20303 Node*& new_node_pt,
20304 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
20305 other_proc_shd_bnd_node_pt,
20306 Vector<unsigned>& other_processor_1,
20307 Vector<unsigned>& other_processor_2,
20308 Vector<unsigned>& other_shared_boundaries,
20309 Vector<unsigned>& other_indexes,
20310 Vector<Vector<Vector<unsigned>>>& global_node_names,
20311 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
20312 Vector<Node*>& global_shared_node_pt)
20313 {
20314 // Get the number of initial shared boundaries to correct the index
20315 // of the shared boundary
20316 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
20317
20318#ifdef PARANOID
20319 // Get the number of instances of the node on other shared
20320 // boundaries with other processors
20321 const unsigned n_data = other_processor_1.size();
20322#endif // #ifdef PARANOID
20323
20324 // Create the first node name
20325 Vector<unsigned> node_name(4);
20326 node_name[0] = other_processor_1[0];
20327 node_name[1] = other_processor_2[0];
20328 node_name[2] = other_shared_boundaries[0];
20329 node_name[3] = other_indexes[0];
20330
20331#ifdef PARANOID
20332 // Get the global node index, and all the names of the node
20333 std::map<Vector<unsigned>, unsigned>::iterator it =
20334 node_name_to_global_index.find(node_name);
20335 if (it == node_name_to_global_index.end())
20336 {
20337 std::ostringstream error_stream;
20338 error_stream << "The node name does not exist in the global node names\n"
20339 << "This is the name of the node\n"
20340 << "Name: iproc, jproc, ishd_bnd, idx\n"
20341 << "Name: " << node_name[0] << ", " << node_name[1] << ", "
20342 << node_name[2] << ", " << node_name[3] << "\n";
20343 throw OomphLibError(
20344 error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
20345 } // if (it!=node_name_to_global_index.end())
20346#endif // #ifdef PARANOID
20347
20348 // Get the global node index
20349 const unsigned iglobal_node = node_name_to_global_index[node_name];
20350 // Add the node to the global shared node container
20351 global_shared_node_pt[iglobal_node] = new_node_pt;
20352 // Get the names
20353 Vector<Vector<unsigned>> inode_names = global_node_names[iglobal_node];
20354 // Get the number of names of the node
20355 const unsigned n_names = inode_names.size();
20356
20357#ifdef PARANOID
20358 // Check that the received names of the node are part of the global
20359 // node names
20360 unsigned n_found_node_names_on_global_node_name = 0;
20361 // loop over the input node names
20362 for (unsigned j = 0; j < n_data; j++)
20363 {
20364 // loop over the inode_names
20365 for (unsigned k = 0; k < n_names; k++)
20366 {
20367 // Is this input name part of the global node names?
20368 if (inode_names[k][0] == other_processor_1[j] &&
20369 inode_names[k][1] == other_processor_2[j] &&
20370 inode_names[k][2] == other_shared_boundaries[j] &&
20371 inode_names[k][3] == other_indexes[j])
20372 {
20373 // Increase the number of found input node names in the
20374 // global node names
20375 n_found_node_names_on_global_node_name++;
20376 }
20377
20378 } // for (k < n_names)
20379
20380 } // for (j < n_data)
20381
20382 // Were all the input node names found on the global node names?
20383 if (n_found_node_names_on_global_node_name != n_data)
20384 {
20385 std::ostringstream error_stream;
20386 error_stream
20387 << "Not all the node names of the current node were found on the\n"
20388 << "global node names. This happened when adding the node pointer\n"
20389 << "to the data structure that keeps tracks of nodes on shared\n"
20390 << "boundaries with other processors\n\n"
20391 << "These are the names of the current node\n"
20392 << "Name k: iproc, jproc, ishd_bnd, idx\n";
20393 for (unsigned j = 0; j < n_data; j++)
20394 {
20395 error_stream << "Name(" << j << "): " << other_processor_1[j] << ", "
20396 << other_processor_2[j] << ", "
20397 << other_shared_boundaries[j] << ", " << other_indexes[j]
20398 << "\n";
20399 }
20400
20401 error_stream << "\n\nThese are the names of the global node\n"
20402 << "Name k: iproc, jproc, ishd_bnd, idx\n";
20403 for (unsigned k = 0; k < n_names; k++)
20404 {
20405 error_stream << "Name(" << k << "): " << inode_names[k][0] << ", "
20406 << inode_names[k][1] << ", " << inode_names[k][2] << ", "
20407 << inode_names[k][3] << "\n";
20408 }
20409
20410 throw OomphLibError(
20411 error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
20412 }
20413#endif // #ifdef PARANOID
20414
20415 // Set the node pointer in all of its names
20416 for (unsigned j = 0; j < n_names; j++)
20417 {
20418 // Get the j-th node name
20419 const unsigned iproc = inode_names[j][0];
20420 const unsigned jproc = inode_names[j][1];
20421 const unsigned ishd_bnd = inode_names[j][2] - initial_shd_bnd_id;
20422 const unsigned index = inode_names[j][3];
20423
20424 // The info. is stored only in one direction
20425 // Get the smallest processor number
20426 if (iproc < jproc)
20427 {
20428 other_proc_shd_bnd_node_pt[iproc][jproc][ishd_bnd][index] = new_node_pt;
20429 }
20430 else
20431 {
20432 other_proc_shd_bnd_node_pt[jproc][iproc][ishd_bnd][index] = new_node_pt;
20433 }
20434
20435 } // for (j < n_names)
20436 }
20437
20438 // *********************************************************************
20439 // End communication functions
20440 // *********************************************************************
20441
20442 // *********************************************************************
20443 // BEGIN: Methods to perform load balance
20444 // *********************************************************************
20445
20446 //======================================================================
20447 /// Performs the load balancing for unstructured meshes, the
20448 /// load balancing strategy is based on mesh migration
20449 //======================================================================
20450 template<class ELEMENT>
20452 const Vector<unsigned>& target_domain_for_local_non_halo_element)
20453 {
20454 oomph_info << "Load balance (unstructured mesh) [BEGIN]" << std::endl;
20455
20456 // This method can only be called when the mesh has been already
20457 // distributed
20458 if (!this->is_mesh_distributed())
20459 {
20460 std::ostringstream warning_message;
20461 warning_message
20462 << "\n===============================================================\n"
20463 << "The load balancing can only be performed in distributed meshes,\n"
20464 << "your mesh has not been distributed.\n"
20465 << "==============================================================="
20466 "\n\n";
20467 OomphLibWarning(warning_message.str(),
20468 OOMPH_CURRENT_FUNCTION,
20469 OOMPH_EXCEPTION_LOCATION);
20470 // Return
20471 return;
20472 }
20473
20474 // Get the number of processors
20475 const unsigned nproc = this->communicator_pt()->nproc();
20476 // Get the rank of the current processors
20477 const unsigned my_rank = this->communicator_pt()->my_rank();
20478
20479 // Check that there are at least two processors
20480 if (nproc == 1)
20481 {
20482 std::ostringstream warning_message;
20483 warning_message
20484 << "\n===============================================================\n"
20485 << "The load balancing can only be performed when there are at least\n"
20486 << "two procesors, the current number of processors is one.\n"
20487 << "==============================================================="
20488 "\n\n";
20489 OomphLibWarning(warning_message.str(),
20490 OOMPH_CURRENT_FUNCTION,
20491 OOMPH_EXCEPTION_LOCATION);
20492 // Return
20493 return;
20494 }
20495
20496 // Get the time before load balance
20497 double t_start_overall_load_balance = 0.0;
20498 if (Print_timings_level_load_balance > 1)
20499 {
20500 t_start_overall_load_balance = TimingHelpers::timer();
20501 }
20502
20503 // Get the number of elements in the mesh before load balance
20504 const unsigned nelement_before_load_balance = this->nelement();
20505
20506#ifdef PARANOID
20507 // The number of elements in the mesh and the number of target
20508 // domains for the local non halo elements in the mesh should match
20509 if (nnon_halo_element() != target_domain_for_local_non_halo_element.size())
20510 {
20511 std::ostringstream error_message;
20512 error_message << "The number of non halo elements in the current mesh ("
20513 << nnon_halo_element() << ") and the number\n"
20514 << "of target areas for the local non halo elements ("
20515 << target_domain_for_local_non_halo_element.size()
20516 << ") is different\n\n";
20517 throw OomphLibError(
20518 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
20519 }
20520#endif
20521
20522 // Backup pointers to elements in this mesh
20523 Vector<FiniteElement*> backed_up_ele_pt(nelement_before_load_balance);
20524 for (unsigned e = 0; e < nelement_before_load_balance; e++)
20525 {
20526 backed_up_ele_pt[e] = this->finite_element_pt(e);
20527 }
20528
20529 // =====================================================================
20530 // BEGIN: GET THE DOMAINS FOR THE HALO ELEMENTS
20531 // =====================================================================
20532
20533 // Get the time to get the domains of halo elements
20534 double tt_start_get_domains_halo_elements = 0.0;
20535 if (Print_timings_level_load_balance > 1)
20536 {
20537 tt_start_get_domains_halo_elements = TimingHelpers::timer();
20538 }
20539
20540 // Get the new domains for the halo elements
20541
20542 // Send the new domains for the current haloed elements, and receive
20543 // the new domains for the current halo elements
20544 // -- 1) On the current processor get the new domains for the
20545 // haloed elements
20546 // -- 2) Then send this info. to all the processor that have a
20547 // halo copy of the element
20548
20549 // The storing for the new domains of the haloed elements (sent to
20550 // other processors)
20551 Vector<Vector<unsigned>> new_domains_haloed_elements(nproc);
20552 // The storing for the new domains of the halo elements (received
20553 // from other processors)
20554 Vector<Vector<unsigned>> new_domains_halo_elements(nproc);
20555
20556 // First resize the containers by getting the current number of
20557 // halo/haloed elements within each processor
20558 for (unsigned iproc = 0; iproc < nproc; iproc++)
20559 {
20560 // There are no halo/haloed elements with myself (my_rank
20561 // processor)
20562 if (iproc != my_rank)
20563 {
20564 // Get the number of halo elements with iproc processor
20565 const unsigned n_halo_iproc = this->nroot_halo_element(iproc);
20566 // Resize the container
20567 new_domains_halo_elements[iproc].resize(n_halo_iproc);
20568
20569 // Get the number of haloed elements with iproc processor
20570 const unsigned n_haloed_iproc = this->nroot_haloed_element(iproc);
20571 // Resize the container
20572 new_domains_haloed_elements[iproc].resize(n_haloed_iproc);
20573 } // if (iproc != my_rank)
20574 } // for (iproc < nproc)
20575
20576#ifdef PARANOID
20577 // Count the number of found haloed elements
20578 Vector<unsigned> counter_for_found_haloed_elements(nproc, 0);
20579#endif
20580
20581 // Go through all the haloed elements and find their new domain
20582
20583 // Get the haloed elements with in each processor and check if the
20584 // element is haloed with the processor
20585 for (unsigned iproc = 0; iproc < nproc; iproc++)
20586 {
20587 // There are no halo/haloed elements with myself (my_rank
20588 // processor)
20589 if (iproc != my_rank)
20590 {
20591 // Get the number of haloed elements with iproc processor
20592 const unsigned n_haloed_iproc = this->nroot_haloed_element(iproc);
20593
20594 // Loop over the haloed elements
20595 for (unsigned ihd = 0; ihd < n_haloed_iproc; ihd++)
20596 {
20597 // Get the ihd-th haloed element with "iproc" processor
20598 GeneralisedElement* haloed_ele_pt =
20599 this->root_haloed_element_pt(iproc, ihd);
20600
20601 // The counter for the nonhalo elements
20602 unsigned nh_count4 = 0;
20603 // Find the element in the general elements container
20604 for (unsigned e = 0; e < nelement_before_load_balance; e++)
20605 {
20606 // Get the e-th element
20607 GeneralisedElement* ele_pt = this->element_pt(e);
20608 // Check if the element is a nonhalo element
20609 if (!ele_pt->is_halo())
20610 {
20611 // Increase the counter for nonhalo elements, in case the
20612 // haloed element is found get the (nh_count4-1) position
20613 // in the target domains vector
20614 nh_count4++;
20615
20616 if (ele_pt == haloed_ele_pt)
20617 {
20618 // Get the new domain for this element
20619 const unsigned element_domain =
20620 target_domain_for_local_non_halo_element[nh_count4 - 1];
20621 // Here decrease the counter ---------------------^
20622
20623 // Set the new domain for the haloed element in the
20624 // special container
20625 new_domains_haloed_elements[iproc][ihd] = element_domain;
20626#ifdef PARANOID
20627 // Increase the counter
20628 counter_for_found_haloed_elements[iproc]++;
20629#endif
20630 // ... and break the "for" with the general
20631 // elements. Continue with the next haloed element
20632 break;
20633
20634 } // if (ele_pt == haloed_ele_pt))
20635
20636 } // if (!ele_pt->is_halo())
20637
20638 } // for (e < nelement_before_load_balance)
20639
20640 } // for (ihd < n_haloed_iproc)
20641
20642 } // if (iproc != my_rank)
20643
20644 } // for (iproc < nproc)
20645
20646#ifdef PARANOID
20647 // Check that all the haloed elements with all processors have been
20648 // found
20649 for (unsigned iproc = 0; iproc < nproc; iproc++)
20650 {
20651 // There are no halo/haloed elements with myself (my_rank
20652 // processor)
20653 if (iproc != my_rank)
20654 {
20655 // Get the number of haloed elements with "iproc" processor
20656 const unsigned n_haloed_iproc = this->nroot_haloed_element(iproc);
20657
20658 // Compare the number of found haloed elements with the current
20659 // number of haloed elements
20660 if (n_haloed_iproc != counter_for_found_haloed_elements[iproc])
20661 {
20662 std::ostringstream error_message;
20663 error_message << "The independent counting of found haloed elements ("
20664 << counter_for_found_haloed_elements[iproc]
20665 << ") with processor (" << iproc
20666 << ") is not equal to the number of haloed elements ("
20667 << n_haloed_iproc << ") with processor (" << iproc
20668 << ")\n";
20669 throw OomphLibError(error_message.str(),
20670 OOMPH_CURRENT_FUNCTION,
20671 OOMPH_EXCEPTION_LOCATION);
20672 } // if (nhaloed_iproc == counter_for_found_haloed_elements[iproc])
20673
20674 } // if (iproc != my_rank)
20675
20676 } // for (iproc < nproc)
20677#endif
20678
20679 // Now we have the new domains for the haloed elements
20680
20681 // Send this info. to the processor with a halo copy of the haloed
20682 // elements and set the new domains in the halo copies
20683
20684 // First put all the info. in a flat package array
20685 Vector<unsigned> new_domains_haloed_flat_unsigned;
20686 // Put in a vector the number of haloed elements within each
20687 // processor
20688 Vector<int> nhaloed_elements_with_iproc(nproc);
20689 for (unsigned iproc = 0; iproc < nproc; iproc++)
20690 {
20691 // There are no halo/haloed elements with myself (my_rank
20692 // processor)
20693 if (iproc != my_rank)
20694 {
20695 // Get the number of haloed elements with "iproc" processor
20696 const unsigned n_haloed_ele_iproc = this->nroot_haloed_element(iproc);
20697 // Copy the number of haloed elements with "iproc" processor
20698 nhaloed_elements_with_iproc[iproc] = n_haloed_ele_iproc;
20699 // Copy the new domains of the haloed elements in the flat
20700 // package
20701 for (unsigned i = 0; i < n_haloed_ele_iproc; i++)
20702 {
20703 new_domains_haloed_flat_unsigned.push_back(
20704 new_domains_haloed_elements[iproc][i]);
20705 } // for (i < n_haloed_ele_iproc)
20706
20707 } // if (iproc != my_rank)
20708
20709 } // for (iproc < nproc)
20710
20711 // The offsets of the flat package within each processor
20712 Vector<int> offset_haloed_elements_with_iproc(nproc);
20713 offset_haloed_elements_with_iproc[0] = 0;
20714 for (unsigned ip = 1; ip < nproc; ip++)
20715 {
20716 // Compute the offset to send the values to each processor
20717 offset_haloed_elements_with_iproc[ip] =
20718 offset_haloed_elements_with_iproc[ip - 1] +
20719 nhaloed_elements_with_iproc[ip - 1];
20720 } // for (ip < nproc)
20721
20722 // Prepare to receive the data
20723
20724 // Compute the number of data (halo elements) to receive from each
20725 // processor and the displacements within each processor
20726
20727 // Counter for the total number of halo elements within all processors
20728 unsigned counter_halo_ele_with_all_procs = 0;
20729
20730 // Put in a vector the number of halo elements expected to receive
20731 // from each processor
20732 Vector<int> nhalo_elements_with_iproc(nproc);
20733 // Compute the number of total halo elements of (my_rank) this
20734 // processor with all other processors
20735 for (unsigned iproc = 0; iproc < nproc; iproc++)
20736 {
20737 // There are no halo/haloed elements with myself (my_rank
20738 // processor)
20739 if (iproc != my_rank)
20740 {
20741 // Get the number of halo elements with "iproc" processor
20742 const unsigned n_halo_ele_iproc = this->nroot_halo_element(iproc);
20743 // Copy the number of halo elements with "iproc" processor
20744 nhalo_elements_with_iproc[iproc] = n_halo_ele_iproc;
20745 // Add the number of elements with this processor
20746 counter_halo_ele_with_all_procs += n_halo_ele_iproc;
20747 } // if (iproc != my_rank)
20748
20749 } // for (iproc < nproc)
20750
20751 // The offsets of the flat package within each processor
20752 Vector<int> offset_halo_elements_with_iproc(nproc);
20753 offset_halo_elements_with_iproc[0] = 0;
20754 for (unsigned ip = 1; ip < nproc; ip++)
20755 {
20756 // Compute the offset to receive the values from each processor
20757 offset_halo_elements_with_iproc[ip] =
20758 offset_halo_elements_with_iproc[ip - 1] +
20759 nhalo_elements_with_iproc[ip - 1];
20760 } // for (ip < nproc)
20761
20762 // The flat container to receive the new domains of the halo
20763 // elements in the current processor
20764
20765 // The flat package where all the info. will be gather from the
20766 // other processors (the halo flat package)
20767 Vector<unsigned> new_domains_halo_flat_unsigned(
20768 counter_halo_ele_with_all_procs);
20769
20770 // Perform the sending and receiving of information to and from all
20771 // processors
20772 MPI_Alltoallv(&new_domains_haloed_flat_unsigned[0], // void *sendbuf
20773 &nhaloed_elements_with_iproc[0], // int *sendcnts
20774 &offset_haloed_elements_with_iproc[0], // int *sdispls
20775 MPI_UNSIGNED, // MPI_Datatype sendtype
20776 &new_domains_halo_flat_unsigned[0], // void *recvbuf
20777 &nhalo_elements_with_iproc[0], // int *recvcnts
20778 &offset_halo_elements_with_iproc[0], // int *rdispls
20779 MPI_UNSIGNED, // MPI_Datatype recvtype
20780 this->communicator_pt()->mpi_comm()); // MPI_Comm comm
20781
20782 // Once received the new domains for the halo elements, copy the
20783 // domains back to an easier to handle container (from the flat
20784 // package to the one with the different halo elements domains
20785 // within each processor)
20786 unsigned counter_new_domains_halo_ele = 0;
20787 for (unsigned iproc = 0; iproc < nproc; iproc++)
20788 {
20789 // There are no halo/haloed elements with myself (my_rank
20790 // processor)
20791 if (iproc != my_rank)
20792 {
20793 // Get the number of halo elements with "iproc"
20794 const unsigned ntmp_halo_elements_with_iproc =
20795 nhalo_elements_with_iproc[iproc];
20796 // Loop over the number of halo elements within "iproc" and copy
20797 // the elements from the flat package
20798 for (unsigned i = 0; i < ntmp_halo_elements_with_iproc; i++)
20799 {
20800 // Copy the new domain of the halo elements from the flat
20801 // package to an easier to use container
20802 new_domains_halo_elements[iproc][i] =
20803 new_domains_halo_flat_unsigned[counter_new_domains_halo_ele++];
20804 }
20805 } // if (iproc != my_rank)
20806 } // for (iproc < nproc)
20807
20808 // The time to get domains of halo elements
20809 if (Print_timings_level_load_balance > 1)
20810 {
20811 oomph_info << "CPU for getting domains halo elements (load balance) [1]: "
20812 << TimingHelpers::timer() - tt_start_get_domains_halo_elements
20813 << std::endl;
20814 }
20815
20816 // =====================================================================
20817 // END: GET THE DOMAINS FOR THE HALO ELEMENTS
20818 // =====================================================================
20819
20820 // =====================================================================
20821 // BEGIN: CREATE FINITE ELEMENT LOCAL VERSIONS OF THE HALO(ED)
20822 // ELEMENTS
20823 // =====================================================================
20824
20825 // Get the time to get FiniteElement versions from Generalised
20826 // halo(ed) elements
20827 double tt_start_get_fe_version_from_ge_halo_ed = 0.0;
20828 if (Print_timings_level_load_balance > 1)
20829 {
20830 tt_start_get_fe_version_from_ge_halo_ed = TimingHelpers::timer();
20831 }
20832
20833 // The finite element storage for the halo elements
20834 Vector<Vector<FiniteElement*>> f_halo_element_pt(nproc);
20835 // The finite element storage for the haloed elements
20836 Vector<Vector<FiniteElement*>> f_haloed_element_pt(nproc);
20837 // Loop over the processors
20838 for (unsigned iproc = 0; iproc < nproc; iproc++)
20839 {
20840 // There are no halo(ed) elements with myself
20841 if (iproc != my_rank)
20842 {
20843 // Get the number of halo elements with the "iproc" processor
20844 const unsigned nhalo_ele_iproc = this->nroot_halo_element(iproc);
20845 // Get the halo elements with the "iproc" processor
20846 Vector<GeneralisedElement*> halo_element_pt_iproc =
20847 this->root_halo_element_pt(iproc);
20848 // Resize the finite element container
20849 f_halo_element_pt[iproc].resize(nhalo_ele_iproc);
20850 // Loop over the halo elements
20851 for (unsigned ih = 0; ih < nhalo_ele_iproc; ih++)
20852 {
20853 // Get the finite element
20854 FiniteElement* ele_pt =
20855 dynamic_cast<FiniteElement*>(halo_element_pt_iproc[ih]);
20856 // Store the finite element version of the element
20857 f_halo_element_pt[iproc][ih] = ele_pt;
20858 } // for (ih < nhalo_ele_iproc)
20859
20860 // Get the number of haloed elements with the "iproc" processor
20861 const unsigned nhaloed_ele_iproc = this->nroot_haloed_element(iproc);
20862 // Get the haloed elements with the "iproc" processor
20863 Vector<GeneralisedElement*> haloed_element_pt_iproc =
20864 this->root_haloed_element_pt(iproc);
20865 // Resize the finite element container
20866 f_haloed_element_pt[iproc].resize(nhaloed_ele_iproc);
20867 // Loop over the haloed elements
20868 for (unsigned ihd = 0; ihd < nhaloed_ele_iproc; ihd++)
20869 {
20870 // Get the finite element
20871 FiniteElement* ele_pt =
20872 dynamic_cast<FiniteElement*>(haloed_element_pt_iproc[ihd]);
20873 // Store the finite element version of the element
20874 f_haloed_element_pt[iproc][ihd] = ele_pt;
20875 } // for (ih < nhaloed_ele_iproc)
20876
20877 } // if (iproc != my_rank)
20878
20879 } // for (iproc < nproc)
20880
20881 // The time to get FiniteElement versions from Generalised halo(ed)
20882 // elements
20883 if (Print_timings_level_load_balance > 1)
20884 {
20885 oomph_info << "CPU for getting finite element versions from generalised "
20886 "halo(ed) elements (load balance) [2]: "
20888 tt_start_get_fe_version_from_ge_halo_ed
20889 << std::endl;
20890 }
20891
20892 // =====================================================================
20893 // END: CREATE FINITE ELEMENT LOCAL VERSIONS OF THE HALO(ED)
20894 // ELEMENTS
20895 // =====================================================================
20896
20897 // =====================================================================
20898 // BEGIN: 1) PREPARE THE ELEMENTS THAT WILL BE SENT TO OTHER PROCESSORS
20899 // ---- HALO ELEMENTS ARE NOT CONSIDERED FOR SENDING
20900 // 2) ASSOCIATE THE NODES WITH THE NEW DOMAIN OF THE ELEMENTS
20901 // ---- THE SAME IS PERFORMED FOR NODES IN HALO ELEMENTS
20902 // =====================================================================
20903
20904 // Get the time to prepare elements to send to other processors
20905 double tt_start_prepare_element_to_send = 0.0;
20906 if (Print_timings_level_load_balance > 1)
20907 {
20908 tt_start_prepare_element_to_send = TimingHelpers::timer();
20909 }
20910
20911 // Store the elements that will be sent to other processors
20912 Vector<Vector<FiniteElement*>> elements_to_send_pt(nproc);
20913
20914 // Associate the nodes of each element with the processor the
20915 // element will live on
20916 std::map<Data*, std::set<unsigned>>
20917 processors_associated_with_data_before_load_balance;
20918
20919 // Compute the elements that will be sent to other processor and
20920 // associate the nodes with the processor the element will live on
20921 unsigned nh_count3 = 0;
20922 for (unsigned e = 0; e < nelement_before_load_balance; e++)
20923 {
20924 // Get the element
20925 FiniteElement* ele_pt = this->finite_element_pt(e);
20926 // Only work with nonhalo elements
20927 if (!(ele_pt->is_halo()))
20928 {
20929 // Get the new domain for the elment
20930 const unsigned element_domain =
20931 target_domain_for_local_non_halo_element[nh_count3++];
20932
20933 // Include the element in the corresponding vector
20934 elements_to_send_pt[element_domain].push_back(ele_pt);
20935
20936 // Get the number of nodes on the element
20937 const unsigned n_nodes = ele_pt->nnode();
20938 // Loop over the nodes
20939 for (unsigned j = 0; j < n_nodes; j++)
20940 {
20941 // Get each node of the element
20942 Node* node_pt = ele_pt->node_pt(j);
20943 // ... and associate it with element domains
20944 processors_associated_with_data_before_load_balance[node_pt].insert(
20945 element_domain);
20946
20947 } // for (j < n_nodes)
20948
20949 } // if (!(ele_pt->is_halo()))
20950
20951 } // for (e < nelement_before_load_balance)
20952
20953 // ... do the same for the halo elements (but do not add them to the
20954 // sending container since only the processor with the haloed
20955 // counterparts is in charge of that). Associate the nodes of the
20956 // halo elements with the processor they will live on
20957 for (unsigned iproc = 0; iproc < nproc; iproc++)
20958 {
20959 // There is no halo elements with myself
20960 if (iproc != my_rank)
20961 {
20962 // Get the number of halo elements with the "iproc" processor
20963 const unsigned n_halo_ele_iproc = this->nroot_halo_element(iproc);
20964 // Get the halo elements with the "iproc" processor
20965 Vector<GeneralisedElement*> halo_element_pt_iproc =
20966 this->root_halo_element_pt(iproc);
20967 // Loop over the halo elements with iproc
20968 for (unsigned ih = 0; ih < n_halo_ele_iproc; ih++)
20969 {
20970 // Get the new domain for the halo element
20971 const unsigned element_domain = new_domains_halo_elements[iproc][ih];
20972
20973 // Get the finite element
20974 FiniteElement* ele_pt =
20975 dynamic_cast<FiniteElement*>(halo_element_pt_iproc[ih]);
20976
20977 // Get the number of nodes on the halo element
20978 const unsigned n_nodes = ele_pt->nnode();
20979 // Loop over the nodes
20980 for (unsigned j = 0; j < n_nodes; j++)
20981 {
20982 // Get each node of the halo element
20983 Node* node_pt = ele_pt->node_pt(j);
20984
20985 // ... and associate it with element domains
20986 processors_associated_with_data_before_load_balance[node_pt].insert(
20987 element_domain);
20988
20989 } // for (j < n_nodes)
20990
20991 } // for (ih < nhalo_ele_iproc)
20992
20993 } // if (iproc != my_rank)
20994
20995 } // for (iproc < nproc)
20996
20997 // The time to prepare elements to send to other processors
20998 if (Print_timings_level_load_balance > 1)
20999 {
21000 oomph_info << "CPU for preparing elements to send to other processors "
21001 "(load balance) [3]: "
21002 << TimingHelpers::timer() - tt_start_prepare_element_to_send
21003 << std::endl;
21004 }
21005
21006 // Now all the nodes are associated with the processor where the
21007 // element will live on. This is performed for the nonhalo and halo
21008 // elements
21009
21010 // =====================================================================
21011 // END: 1) PREPARE THE ELEMENTS THAT WILL BE SENT TO OTHER PROCESSORS
21012 // ---- HALO ELEMENTS ARE NOT CONSIDERED FOR SENDING
21013 // 2) ASSOCIATE THE NODES WITH THE NEW DOMAIN OF THE ELEMENTS
21014 // ---- THE SAME IS PERFORMED FOR NODES IN HALO ELEMENTS
21015 // =====================================================================
21016
21017 // =====================================================================
21018 // BEGIN: COMPUTE THE NEW LOCAL HALO ELEMENTS OF ALL PROCESSORS IN THE
21019 // CURRENT PROCESSOR
21020 // ----- FOR NONHALO ELEMENTS AND FOR HALO ELEMENTS
21021 // =====================================================================
21022
21023 // Get the time to compute new local halo elements within all
21024 // processors
21025 double tt_start_compute_new_local_halo_elements = 0.0;
21026 if (Print_timings_level_load_balance > 1)
21027 {
21028 tt_start_compute_new_local_halo_elements = TimingHelpers::timer();
21029 }
21030
21031 // Before sending the elements across compute the new local
21032 // halo/haloed elements of each processor. Each processor could have
21033 // elements that will be part of the new halo/haloed elements of
21034 // another processors, then these processors need to compute the
21035 // relations that may happen among these other processors
21036
21037 // Example:
21038 // Processor 1 may have elements that will be sent to processor 3
21039 // and 4. These processors need to know about the new halo elements
21040 // betweeen them but at this moment only processor 1 can compute that
21041 // info., since it is the only one that currently has that info.
21042
21043 // Store the new local-halo elements of each processor, the HALOED
21044 // elements are also stored in the container, only needs to INVERT
21045 // the indexes. For example, the HALO elements of processor 2 with
21046 // processor 3 are stored in new_local_halo_element_pt[2][3], and
21047 // the HALOED elements of processor 2 with processor 3 are stored in
21048 // new_local_halo_element_pt[3][2]. Notice that these are also the
21049 // halo elements of processor 3 with 2
21050
21051 // How to identify the new local halo/haloed element: 1) Loop over
21052 // the element; 2) Only work with nonhalo elements; 3) If the
21053 // element is not assigned to the current processor (iproc) then
21054 // check; 4) Is one of its nodes assiociated to the iproc processor?
21055 // 5) If yes the element is a halo in the iproc processor whose
21056 // nonhalo counter part (haloed) lives in the domain assigned to the
21057 // element
21058 Vector<Vector<Vector<FiniteElement*>>> new_local_halo_element_pt(nproc);
21059
21060 // Loop over the processors
21061 for (unsigned iproc = 0; iproc < nproc; iproc++)
21062 {
21063 // Resize the container
21064 new_local_halo_element_pt[iproc].resize(nproc);
21065
21066 // Boolean to know which elements have been already added to the
21067 // new local halo scheme in "iproc"
21068 Vector<std::map<FiniteElement*, bool>> new_local_halo_already_added(
21069 nproc);
21070
21071 // Go through all the elements and identify the new local halo
21072 // elements of "iproc"
21073 unsigned nh_count5 = 0;
21074 for (unsigned e = 0; e < nelement_before_load_balance; e++)
21075 {
21076 // Get the element
21077 FiniteElement* ele_pt = this->finite_element_pt(e);
21078 // Only work with nonhalo elements
21079 if (!(ele_pt->is_halo()))
21080 {
21081 // Get the domain to which the current element is associated
21082 const unsigned ele_domain =
21083 target_domain_for_local_non_halo_element[nh_count5++];
21084 // If the current element is not associated to the "iproc"
21085 // processor then it could be a halo element
21086 if (ele_domain != iproc)
21087 {
21088 // Get the number of nodes
21089 const unsigned nnodes = ele_pt->nnode();
21090 // Loop over the nodes
21091 for (unsigned j = 0; j < nnodes; j++)
21092 {
21093 Node* node_pt = ele_pt->node_pt(j);
21094 // Check if the node is associated with the current
21095 // "iproc" processor
21096 std::set<unsigned>::iterator it =
21097 processors_associated_with_data_before_load_balance[node_pt]
21098 .find(iproc);
21099 // If it is found then the element is a halo-element
21100 if (it !=
21101 processors_associated_with_data_before_load_balance[node_pt]
21102 .end())
21103 {
21104 // Add the element as new local-halo element with the
21105 // "ele_domain" processor. The non-halo counterpart will
21106 // be located on "ele_domain" processor after sending
21107 // elements across
21108 if (!new_local_halo_already_added[ele_domain][ele_pt])
21109 {
21110 // The element is a halo element on "iproc" with
21111 // "ele_domain"
21112 new_local_halo_element_pt[iproc][ele_domain].push_back(
21113 ele_pt);
21114 // Mark as done
21115 new_local_halo_already_added[ele_domain][ele_pt] = true;
21116 } // if (!new_local_halo_already_added[ele_domain][ele_pt])
21117 } // One of the nodes lies on an element on the current
21118 // "iproc" processor
21119 } // for (j < nnodes)
21120 } // if (ele_domain != iproc)
21121 } // if (!(ele_pt->is_halo()))
21122 } // for (e < nelement_before_load_balance)
21123
21124 // Now do the same with the halo elements, we need to find those
21125 // halo elements that continue being halo elements but possibly
21126 // with/on another processor. The pair of processors where a
21127 // possible shared boundary is created needs to be notified.
21128
21129 // Example
21130 //
21131 // ---------------* *---------------
21132 // | |* *| |
21133 // | |* *| |
21134 // | New domain |* *| New domain | * Mark the position
21135 // | proc 1 |* *| proc 3 | of halo elements
21136 // | |* *| |
21137 // | |* *| |
21138 // ---------------* *---------------
21139 // Proc 1 Proc 2
21140
21141 // Processor 1: The halo elements on processor 1 continue being halo ON
21142 // PROCESSOR 1, but now WITH PROCESSOR 3
21143
21144 // Processor 2: The halo elements on processor 2 continue being
21145 // halo BUT now ON PROCESSOR 3 WITH PROCESSOR 1
21146
21147 // The current processor (my_rank) also needs to consider the halo
21148 // elements that will be halo elements of other processor with
21149 // another processor. The case of processor 2
21150
21151 // Loop over all the halo elements in the current processor and
21152 // check if they will be halo with the "iproc" processor
21153 for (unsigned jproc = 0; jproc < nproc; jproc++)
21154 {
21155 // There are no halo elements with myself (the old halo elements
21156 // were halo in the "my_rank" processor)
21157 if (jproc != my_rank)
21158 {
21159 // Get the number of halo elements with the "jproc" processor
21160 const unsigned n_halo_ele_jproc = this->nroot_halo_element(jproc);
21161 // Get the halo elements with the "jproc" processor
21162 Vector<GeneralisedElement*> halo_element_pt_jproc =
21163 this->root_halo_element_pt(jproc);
21164 // ... and check if any of those elements is a new halo
21165 // element with the "iproc" processor
21166 for (unsigned jh = 0; jh < n_halo_ele_jproc; jh++)
21167 {
21168 // Get the new domain for the halo element
21169 const unsigned ele_domain = new_domains_halo_elements[jproc][jh];
21170
21171 // If the current element is not associated to the "iproc"
21172 // processor then it could be a halo element on "iproc" with
21173 // "ele_domain".
21174
21175 // NOTE OUTDATE: Check if the halo element is going to be
21176 // sent to this processor (my_rank), if that is the case
21177 // then we don't need to add it to the set of new halo
21178 // elements with any other processor since any possible
21179 // shared boundary will be created when checking for the
21180 // intersection of the sent and received elements
21181
21182 // if (ele_domain != iproc && ele_domain != my_rank)
21183
21184 // NOTE UPDATE: Only check if the halo element is not going
21185 // to be part of the iproc processor, not required to avoid
21186 // those halo elements whose domain is the current rank
21187 // (my_rank). When the shared boundaries are computed, these
21188 // last elements can not create a shared boundary since no
21189 // haloed elements (that shared an edge) are found for
21190 // them. By considering also those halo elements whose new
21191 // domain is the current one (commenting "ele_domain !=
21192 // my_rank") the current processor can compute shared
21193 // boundaries with the iproc processor with help of its old
21194 // halo elements but that will become nonhalo elements, in
21195 // fact they will become haloed elements The halo element is
21196 // not sent to the "element_domain" processor and is not
21197 // passed to the array used to create the new shared
21198 // boundaries "new_shared_boundary_element_pt" because of
21199 // its halo condition
21200 if (ele_domain != iproc)
21201 {
21202 // Get the finite element
21203 FiniteElement* ele_pt =
21204 dynamic_cast<FiniteElement*>(halo_element_pt_jproc[jh]);
21205 // Get the number of nodes on the halo element
21206 const unsigned nnodes = ele_pt->nnode();
21207 // Loop over the nodes
21208 for (unsigned j = 0; j < nnodes; j++)
21209 {
21210 // Get each node of the halo element
21211 Node* node_pt = ele_pt->node_pt(j);
21212
21213 // Check if the node is associated with the "iproc"
21214 // processor
21215 std::set<unsigned>::iterator it =
21216 processors_associated_with_data_before_load_balance[node_pt]
21217 .find(iproc);
21218 // If it is found then the element is a halo-element
21219 if (it !=
21220 processors_associated_with_data_before_load_balance[node_pt]
21221 .end())
21222 {
21223 // Add the element as new local-halo element with
21224 // the "ele_domain" processor. The non-halo
21225 // counterpart will be located on "ele_domain"
21226 // processor. Because this is a old-halo element it
21227 // will not be sent to the "element_domain" processor
21228 if (!new_local_halo_already_added[ele_domain][ele_pt])
21229 {
21230 // The element is a halo element on "iproc" with
21231 // "ele_domain"
21232 new_local_halo_element_pt[iproc][ele_domain].push_back(
21233 ele_pt);
21234 new_local_halo_already_added[ele_domain][ele_pt] = true;
21235
21236 // Break the for of the nodes, the element has been
21237 // already added to the new_local_halo_element_pt
21238 // structure
21239 break;
21240
21241 } // if (!new_local_halo_already_added[ele_domain][ele_pt])
21242
21243 } // One of the nodes lies on an element belonging to
21244 // "iproc" processor
21245
21246 } // for (j < nnodes)
21247
21248 } // if (ele_domain != iproc)
21249
21250 } // for (jh < n_halo_ele_jproc)
21251
21252 } // if (jproc != my_rank) // The old halo elements are halo
21253 // with other processors except with "my_rank"
21254
21255 } // for (jproc < nproc): This is the one that goes for the halo
21256 // elements in the current processor to find the new halo
21257 // elements
21258
21259 } // for (iproc < nproc)
21260
21261 // Get the time to compute new local halo elements within all
21262 // processors
21263 if (Print_timings_level_load_balance > 1)
21264 {
21266 << "CPU for computing new local halo elements (load balance) [4]: "
21267 << TimingHelpers::timer() - tt_start_compute_new_local_halo_elements
21268 << std::endl;
21269 }
21270
21271 // =====================================================================
21272 // END: COMPUTE THE NEW LOCAL HALO ELEMENTS OF ALL PROCESSORS IN THE
21273 // CURRENT PROCESSOR
21274 // ----- FOR NONHALO ELEMENTS AND FOR HALO ELEMENTS
21275 // =====================================================================
21276
21277 // =====================================================================
21278 // BEGIN: COMPUTE THE NEW LOCAL SHARED BOUNDARY ELEMENTS AND THE
21279 // FACE ELEMENTS. THE SUBSET OF THE ELEMENTS TO SENT THAT ARE PART
21280 // OF THE NEW LOCAL SHARED BOUNDARY ELEMENTS ARE IDENTIFIED TO BE
21281 // MARKED AS HALOED ELEMENTS AND BELONGING TO THE SHARED BOUNDARY
21282 // ELEMENTS IN THE RECEIVED PROCESSOR
21283 // =====================================================================
21284
21285 // Get the time to compute new local shared boundary elements
21286 double tt_start_compute_new_local_shd_bnd_ele = 0.0;
21287 if (Print_timings_level_load_balance > 1)
21288 {
21289 tt_start_compute_new_local_shd_bnd_ele = TimingHelpers::timer();
21290 }
21291
21292 // Store the new local-shared boundary elements and the face indexes
21293 // The halo elements and halo face indexes
21295 new_local_halo_shared_boundary_element_pt(nproc);
21297 new_local_halo_shared_boundary_element_face_index(nproc);
21298
21299 // Allocate enough memory for the containers
21300 for (unsigned iproc = 0; iproc < nproc; iproc++)
21301 {
21302 new_local_halo_shared_boundary_element_pt[iproc].resize(nproc);
21303 new_local_halo_shared_boundary_element_face_index[iproc].resize(nproc);
21304 } // for (iproc < nproc)
21305
21306 // Get the elements that create the new local-halo-shared
21307 // boundaries, mark them and identify the face that lies on the
21308 // shared boundary. The new local-halo-shared boundary elements are
21309 // actually a sub-set of the halo elements of each processor with in
21310 // each processor
21311 for (unsigned iproc = 0; iproc < nproc; iproc++)
21312 {
21313 // Star from jproc = iproc + 1 to avoid double creation of shared
21314 // boundary elements, any shared boundary element identified
21315 // between processor "iproc" and "jproc" is also established as
21316 // shared boundary element between processor "jproc" and "iproc"
21317 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
21318 {
21319 this->get_shared_boundary_elements_and_face_indexes(
21320 new_local_halo_element_pt[iproc][jproc],
21321 new_local_halo_element_pt[jproc][iproc],
21322 new_local_halo_shared_boundary_element_pt[iproc][jproc],
21323 new_local_halo_shared_boundary_element_face_index[iproc][jproc],
21324 new_local_halo_shared_boundary_element_pt[jproc][iproc],
21325 new_local_halo_shared_boundary_element_face_index[jproc][iproc]);
21326 } // for (jproc < nproc)
21327 } // for (iproc < nproc)
21328
21329 // The time to compute new local shared boundary elements
21330 if (Print_timings_level_load_balance > 1)
21331 {
21332 oomph_info << "CPU for computing new local shared boundary elements "
21333 "(load balance) [5]: "
21335 tt_start_compute_new_local_shd_bnd_ele
21336 << std::endl;
21337 }
21338
21339 // =====================================================================
21340 // END: COMPUTE THE NEW LOCAL SHARED BOUNDARY ELEMENTS AND THE FACE
21341 // ELEMENTS. THE SUBSET OF THE ELEMENTS TO SENT THAT ARE PART OF THE
21342 // NEW LOCAL SHARED BOUNDARY ELEMENTS ARE IDENTIFIED TO BE MARKED AS
21343 // HALOED ELEMENTS AND BELONGING TO THE SHARED BOUNDARY ELEMENTS IN
21344 // THE RECEIVED PROCESSOR
21345 // =====================================================================
21346
21347 // =====================================================================
21348 // BEGIN: SEND THE ELEMENTS AND IDENTIFY THOSE THAT ARE PART OF THE
21349 // SHARED BOUNDARIES AND HALOED WITH OTHER PROCESSORS
21350 // =====================================================================
21351
21352 // Get the time to send the elements to their new processor in
21353 // charge
21354 double tt_start_send_elements_to_other_processors = 0.0;
21355 if (Print_timings_level_load_balance > 1)
21356 {
21357 tt_start_send_elements_to_other_processors = TimingHelpers::timer();
21358 }
21359
21360 // Sort the nodes on shared boundaries so that they have the same
21361 // order on all the shared boundaries, this is required to know the
21362 // possible shared nodes among processors
21363 this->sort_nodes_on_shared_boundaries();
21364
21365 // Store the received elements from each processor
21366 Vector<Vector<FiniteElement*>> received_elements_pt(nproc);
21367
21368 // The haloed elements and haloed face indexes, these store the
21369 // haloed elements received from "iproc" but that are haloed with
21370 // "jproc". The elements are received from "iproc" which was the
21371 // processor that computed the haloed relation of the "my_rank"
21372 // processor with "jproc"
21374 new_received_haloed_shared_boundary_element_pt(nproc);
21376 new_received_haloed_shared_boundary_element_face_index(nproc);
21377
21378 // Container where to store the nodes on shared boundaries not
21379 // associated with the processor that receives the elements/nodes
21380 // other_proc_shd_bnd_node_pt[iproc][jproc][shd_bnd_id][index]
21382 other_proc_shd_bnd_node_pt(nproc);
21383 // Resize the container
21384 for (unsigned iproc = 0; iproc < nproc; iproc++)
21385 {
21386 // Resize the container
21387 other_proc_shd_bnd_node_pt[iproc].resize(nproc);
21388 for (unsigned jproc = 0; jproc < nproc; jproc++)
21389 {
21390 // Get the number of shared boundaries (OLD shared boundaries)
21391 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
21392 const unsigned final_shd_bnd_id = this->final_shared_boundary_id();
21393 const unsigned n_shared_bound = final_shd_bnd_id - initial_shd_bnd_id;
21394 other_proc_shd_bnd_node_pt[iproc][jproc].resize(n_shared_bound);
21395 } // for (jproc < nproc)
21396
21397 } // for (iproc < nproc)
21398
21399 // Store the global node names
21400 // global_node_name[x][ ][ ] Global node number
21401 // global_node_name[ ][x][ ] Global node names
21402 // global_node_name[ ][ ][x] Global node info.
21403 Vector<Vector<Vector<unsigned>>> global_node_names;
21404
21405 // Creates a map between the node name and the index of the global
21406 // node so we can access all its node names
21407 std::map<Vector<unsigned>, unsigned> node_name_to_global_index;
21408
21409 // Store the global shared nodes pointers
21410 Vector<Node*> global_shared_node_pt;
21411
21412 // Compute all the names of the nodes and fill in the
21413 // "other_proc_shd_bnd_node_pt" structure with the nodes that live
21414 // on this processor (my_rank) by looking over all their names
21415 compute_global_node_names_and_shared_nodes(other_proc_shd_bnd_node_pt,
21416 global_node_names,
21417 node_name_to_global_index,
21418 global_shared_node_pt);
21419
21420 // From the elements received from each processor, store the haloed
21421 // information of the element, it means, the processor with which it
21422 // is haloed and the haloed index with that processor
21424 received_old_haloed_element_pt(nproc);
21425 // [x][][] : The receiver processor (the original processor)
21426 // [][x][] : The processor with which the receiver processor has
21427 // haloed elements
21428 // [][][x]: The haloed element number
21429
21430 // Resize the container
21431 for (unsigned iproc = 0; iproc < nproc; iproc++)
21432 {
21433 received_old_haloed_element_pt[iproc].resize(nproc);
21434 } // for (iproc < nproc)
21435
21436 // Go through all processors and send the corresponding elements to
21437 // each one
21438 for (unsigned iproc = 0; iproc < nproc; iproc++)
21439 {
21440 if (iproc != my_rank)
21441 {
21442 // -----------------------------------------------------------
21443 // Send (package) information of the elements
21444 // -----------------------------------------------------------
21445
21446 // Keep track of the currently sent elements
21447 Vector<FiniteElement*> currently_sent_elements;
21448 // Keep track of the currently sent nodes to the iproc processor
21449 Vector<Node*> currently_sent_nodes;
21450
21451 // Clear send and receive buffers
21452 Flat_packed_unsigneds.clear();
21453 Flat_packed_doubles.clear();
21454#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21456#endif
21457
21458 // Get the number of elements to send to iproc processor
21459 const unsigned nelements_to_send = elements_to_send_pt[iproc].size();
21460
21461 // The very first data of the flat package sent to processor
21462 // iproc is the number of elements that will be sent, this data
21463 // is used by the receiver processor to loop over the number of
21464 // expected elements to receive
21465 Flat_packed_unsigneds.push_back(nelements_to_send);
21466#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21467 std::stringstream junk;
21468 junk << "Number of elements to send from processor " << my_rank
21469 << " to processor " << iproc << ": (" << nelements_to_send << ")";
21470 Flat_packed_unsigneds_string.push_back(junk.str());
21471#endif
21472
21473 // Loop over the elements to sent
21474 for (unsigned e = 0; e < nelements_to_send; e++)
21475 {
21476 // Get the element to send
21477 FiniteElement* send_ele_pt = elements_to_send_pt[iproc][e];
21478
21479 // Get the current number of sent elements
21480 const unsigned ncurrently_sent_elements =
21481 currently_sent_elements.size();
21482
21483 // Try to add the element
21484 const unsigned index_ele = try_to_add_element_pt_load_balance(
21485 currently_sent_elements, send_ele_pt);
21486
21487 // Element needs to be added
21488 if (index_ele == ncurrently_sent_elements)
21489 {
21490 Flat_packed_unsigneds.push_back(1);
21491#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21493 "Element needs to be constructed");
21494#endif
21495
21496 // Get required info. related with the element
21497 get_required_elemental_information_load_balance_helper(
21498 iproc, f_haloed_element_pt, send_ele_pt);
21499
21500 // Get the number of nodes in the element
21501 const unsigned nnodes = send_ele_pt->nnode();
21502
21503 // Loop over the nodes in the element
21504 for (unsigned j = 0; j < nnodes; j++)
21505 {
21506 Node* node_pt = send_ele_pt->node_pt(j);
21507
21508 // Package the info. of the nodes
21509 add_node_load_balance_helper(iproc, // The destination process
21510 f_halo_element_pt,
21511 currently_sent_nodes,
21512 node_pt);
21513
21514 } // for (j < nnodes)
21515
21516 } // if (index_ele == ncurrently_sent_elements)
21517 else
21518 {
21519 Flat_packed_unsigneds.push_back(0);
21520#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21521 Flat_packed_unsigneds_string.push_back("Element already exists");
21522#endif
21523 Flat_packed_unsigneds.push_back(index_ele);
21524#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21525 Flat_packed_unsigneds_string.push_back("Index of existing element");
21526#endif
21527 } // else if (index_ele == ncurrently_sent_elements)
21528
21529 } // for (e < nelements_to_send)
21530
21531 // After storing the info. of the elements identify the indexes
21532 // of the "new_local_halo_shared_boundary_elements" in the
21533 // "currently_send_elements" vector, these elements will be
21534 // identified as "new_received_haloed_shared_boundary_elements"
21535 // on the "receiver" processor
21536
21537 // Each processor has information of every other processor so we
21538 // need to send all the corresponding info. to the other
21539 // processors. Processor 1 may have information of the relation
21540 // (halo elements) between processor 3 and 4 say, so processor 1
21541 // needs to let know processor 3 and 4 what this relation is
21542 // (which are the shared-elements among these processors)
21543
21544 for (unsigned jproc = 0; jproc < nproc; jproc++)
21545 {
21546 // Get the number of new local-halo shared boundary elements
21547 // between processor "jproc" and "iproc" (we invert the index
21548 // since we really want the haloed elements, those elements
21549 // that we have just sent)
21550 const unsigned njproc_iproc_new_local_halo_shared_boundary_ele =
21551 new_local_halo_shared_boundary_element_pt[jproc][iproc].size();
21552
21553 // The vector with the info. of the indexes
21554 Vector<unsigned> new_local_halo_shared_boundary_ele_index;
21555
21556 // The number of found shared boundary elements in the sent
21557 // container (only consider the nonhalo elements)
21558 unsigned nfound_new_local_halo_shared_bound_ele_index = 0;
21559 // The number of nonhalo elements in the new local halo shared
21560 // boundary elements
21561 unsigned nnon_halo_new_local_halo_shared_bound_ele = 0;
21562
21563 // Loop over the local halo shared boundary elements between
21564 // processor jproc and iproc
21565 for (unsigned e = 0;
21566 e < njproc_iproc_new_local_halo_shared_boundary_ele;
21567 e++)
21568 {
21569 // Get the shared boundary element
21570 FiniteElement* shared_ele_pt =
21571 new_local_halo_shared_boundary_element_pt[jproc][iproc][e];
21572
21573 // Only consider the nonhalo elements since the halo
21574 // elements were no considered for sending
21575 if (!shared_ele_pt->is_halo())
21576 {
21577 nnon_halo_new_local_halo_shared_bound_ele++;
21578
21579 // Now find the index on the currently sent elements
21580
21581 // Get the current number of sent elements
21582 const unsigned ncurrently_sent_elements =
21583 currently_sent_elements.size();
21584 // Loop over the sent elements
21585 for (unsigned ics = 0; ics < ncurrently_sent_elements; ics++)
21586 {
21587 FiniteElement* currently_sent_ele_pt =
21588 currently_sent_elements[ics];
21589
21590 // Is this the element?
21591 if (currently_sent_ele_pt == shared_ele_pt)
21592 {
21593 // Store the index on the sent elements of the local
21594 // halo shared boundary element
21595 new_local_halo_shared_boundary_ele_index.push_back(ics);
21596 // Increase the number of found new local halo shared
21597 // bound element index
21598 nfound_new_local_halo_shared_bound_ele_index++;
21599 // We have found it, no need to further search
21600 break;
21601 } // if (currently_sent_ele_pt == shared_ele_pt)
21602
21603 } // for (ics < ncurrently_sent_elements)
21604
21605 } // if (!shared_ele_pt->is_halo())
21606
21607 } // for (e < niproc_new_local_halo_shared_boundary_ele)
21608
21609#ifdef PARANOID
21610 if (nfound_new_local_halo_shared_bound_ele_index !=
21611 nnon_halo_new_local_halo_shared_bound_ele)
21612 {
21613 std::ostringstream error_message;
21614 error_message << "Was only possible to identify ("
21615 << nfound_new_local_halo_shared_bound_ele_index
21616 << ") of ("
21617 << nnon_halo_new_local_halo_shared_bound_ele
21618 << ") shared "
21619 << "elements between\nprocessor (" << iproc
21620 << ") and (" << jproc << ") "
21621 << "when sending elements to processor (" << iproc
21622 << ")\n\n";
21623 throw OomphLibError(error_message.str(),
21624 OOMPH_CURRENT_FUNCTION,
21625 OOMPH_EXCEPTION_LOCATION);
21626 }
21627#endif
21628
21629 // Send a flag for synchronisation issues
21630 Flat_packed_unsigneds.push_back(9999);
21631#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21632 std::stringstream junk;
21633 junk << "Flag for synchronisation 9999";
21634 Flat_packed_unsigneds_string.push_back(junk.str());
21635#endif
21636
21637 // Send the number of nonhalo new local-shared boundary
21638 // elements of processor "iproc" with processor "jproc"
21639 Flat_packed_unsigneds.push_back(
21640 nnon_halo_new_local_halo_shared_bound_ele);
21641#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21642 std::stringstream junk2;
21643 junk2 << "Number of new local halo shared boundary elements "
21644 << nnon_halo_new_local_halo_shared_bound_ele;
21645 Flat_packed_unsigneds_string.push_back(junk2.str());
21646#endif
21647
21648 // Send the indexes and the face indexes of the shared
21649 // boundary elements
21650 unsigned counter_nonhalo_sent = 0;
21651 // Loop over the local halo shared boundary elements between
21652 // processor jproc and iproc
21653 for (unsigned e = 0;
21654 e < njproc_iproc_new_local_halo_shared_boundary_ele;
21655 e++)
21656 {
21657 // Get the shared boundary element
21658 FiniteElement* shared_ele_pt =
21659 new_local_halo_shared_boundary_element_pt[jproc][iproc][e];
21660
21661 // Only consider the nonhalo elements since the halo
21662 // elements were no considered for sending
21663 if (!shared_ele_pt->is_halo())
21664 {
21665 // Get the index on the sent elements of the current
21666 // nonhalo shared boundary element
21667 const unsigned ele_index =
21668 new_local_halo_shared_boundary_ele_index
21669 [counter_nonhalo_sent++];
21670 // ... and get the face index
21671 const unsigned face_index =
21672 new_local_halo_shared_boundary_element_face_index[jproc][iproc]
21673 [e];
21674
21675 // Send the index on the sent elements of the new local
21676 // halo shared boundary element
21677 Flat_packed_unsigneds.push_back(ele_index);
21678#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21679 std::stringstream junk;
21680 junk << "The index of the halo shared boundary element "
21681 << ele_index;
21682 Flat_packed_unsigneds_string.push_back(junk.str());
21683#endif
21684
21685 // Send the face index of the new local halo shared boundary
21686 // element
21687 Flat_packed_unsigneds.push_back(face_index);
21688#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21689 std::stringstream junk2;
21690 junk2 << "The face index of the halo shared boundary element "
21691 << face_index;
21692 Flat_packed_unsigneds_string.push_back(junk2.str());
21693#endif
21694
21695 } // if (!shared_ele_pt->is_halo())
21696
21697 } // for (e < niproc_new_local_halo_shared_boundary_ele)
21698
21699 } // for (jproc < nproc)
21700
21701 // ----------------------------------------------------------
21702 // Send the info. perform the communications
21703 // ----------------------------------------------------------
21704 // Processor to which send the info.
21705 int send_proc = static_cast<int>(iproc);
21706 // Processor from which receive the info.
21707 int recv_proc = static_cast<int>(iproc);
21708 send_and_receive_elements_nodes_info(send_proc, recv_proc);
21709
21710 // ----------------------------------------------------------
21711 // Receive (unpackage) the info of the elements
21712 // ----------------------------------------------------------
21713
21714 // Keep track of the currently created elements
21715 Vector<FiniteElement*> currently_created_elements;
21716 // Keep track of the currently created nodes
21717 Vector<Node*> currently_created_nodes;
21718
21719 // Reset the counters
21722
21723#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21725 << " Number of elements need to be constructed "
21727 << std::endl;
21728#endif
21729
21730 // Read the number of elements that need to be created
21731 const unsigned nelements_to_create =
21733
21734 for (unsigned e = 0; e < nelements_to_create; e++)
21735 {
21736 // Create the element from received info. of "iproc"
21737 // processor on the current processor
21738 create_element_load_balance_helper(iproc,
21739 f_haloed_element_pt,
21740 received_old_haloed_element_pt,
21741 currently_created_elements,
21742 currently_created_nodes,
21743 other_proc_shd_bnd_node_pt,
21744 global_node_names,
21745 node_name_to_global_index,
21746 global_shared_node_pt);
21747 }
21748
21749 // Copy the received elements from "iproc" processor
21750
21751 // Number of received elements
21752 const unsigned nreceived_elements = currently_created_elements.size();
21753 received_elements_pt[iproc].resize(nreceived_elements);
21754 for (unsigned e = 0; e < nreceived_elements; e++)
21755 {
21756 received_elements_pt[iproc][e] = currently_created_elements[e];
21757 }
21758
21759 // Go for the haloed elements received from processor "iproc"
21760 // but haloed with "jproc"
21761
21762 // Allocate memory for the containers
21763 new_received_haloed_shared_boundary_element_pt[iproc].resize(nproc);
21764 new_received_haloed_shared_boundary_element_face_index[iproc].resize(
21765 nproc);
21766
21767 // Loop over the processors
21768 for (unsigned jproc = 0; jproc < nproc; jproc++)
21769 {
21770 // Read the synchronisation flag
21771 const unsigned synchronisation_flag =
21773
21774 if (synchronisation_flag != 9999)
21775 {
21776 std::ostringstream error_message;
21777 error_message << "The synchronisation flag was not read, the\n"
21778 << "information sent between processor (" << my_rank
21779 << ") "
21780 << "and (" << iproc
21781 << ")\nis no longer synchronised\n\n";
21782 throw OomphLibError(error_message.str(),
21783 OOMPH_CURRENT_FUNCTION,
21784 OOMPH_EXCEPTION_LOCATION);
21785 }
21786
21787 // Read the number of elements that will be part of the new
21788 // received haloed shared boundary elements received from "iproc"
21789 // and haloed with "jproc"
21790 const unsigned niproc_jproc_new_received_haloed_shared_boundary_ele =
21792
21793 // Loop over the new received haloed shared boundary elements
21794 for (unsigned e = 0;
21795 e < niproc_jproc_new_received_haloed_shared_boundary_ele;
21796 e++)
21797 {
21798 // Read the index of the new received haloed shared boundary
21799 // ele with "jproc"
21800 const unsigned ele_index =
21802 // Read the face index for the new received haloed shared
21803 // boundary element
21804 const unsigned face_index =
21806
21807 // Get the element
21808 FiniteElement* shared_ele_pt =
21809 currently_created_elements[ele_index];
21810
21811 // Add the element to the new received-haloed shared
21812 // boundary elements. Received from "iproc" but haloed with
21813 // "jproc" processor
21814 new_received_haloed_shared_boundary_element_pt[iproc][jproc]
21815 .push_back(shared_ele_pt);
21816 // Store the face index
21817 new_received_haloed_shared_boundary_element_face_index[iproc][jproc]
21818 .push_back(face_index);
21819
21820 } // for (e < niproc_jproc_read_new_local_shared_boundary_ele)
21821
21822 } // for (jproc < nproc)
21823
21824 } // if (iproc != my_rank)
21825
21826 } // for (iproc < nproc)
21827
21828 // The time to send the elements to their new processor in charge
21829 if (Print_timings_level_load_balance > 1)
21830 {
21831 oomph_info << "CPU for sending elements to their new processors (load "
21832 "balance) [6]: "
21834 tt_start_send_elements_to_other_processors
21835 << std::endl;
21836 }
21837
21838 // =====================================================================
21839 // END: SEND THE ELEMENTS AND IDENTIFY THOSE THAT ARE PART OF THE
21840 // SHARED BOUNDARIES AND HALOED WITH OTHER PROCESSORS
21841 // =====================================================================
21842
21843 // =====================================================================
21844 // BEGIN: GET ANY ADDITIONAL SHARED BOUNDARY BY THE INTERSECTION OF
21845 // THE ELEMENTS SENT TO PROCESSOR "IPROC" AND THE ELEMENTS RECEIVED
21846 // FROM PROCESSOR "IPROC". IF ANY NEW SHARED BOUNDARY IS FOUND, IT
21847 // IS CREATED BY THE OLD HALO ELEMENTS (RECEIVED ELEMENTS) THAT HAVE
21848 // NOW BECOME PART OF THE DOMAIN AND THE OLD HALOED ELEMENTS (SENT
21849 // ELEMENTS)
21850 // =====================================================================
21851
21852 // Get the time to compute any additional shared boundary
21853 double tt_start_compute_additional_shared_boundaries = 0.0;
21854 if (Print_timings_level_load_balance > 1)
21855 {
21856 tt_start_compute_additional_shared_boundaries = TimingHelpers::timer();
21857 }
21858
21859 // Store any additional elements that may create a shared boundary,
21860 // after sending elements from one to other processor check for any
21861 // new possible shared boundaries
21862 Vector<Vector<FiniteElement*>> tmp_group1_shared_boundary_element_pt(nproc);
21863 Vector<Vector<unsigned>> tmp_group1_shared_boundary_element_face_index(
21864 nproc);
21865 Vector<Vector<FiniteElement*>> tmp_group2_shared_boundary_element_pt(nproc);
21866 Vector<Vector<unsigned>> tmp_group2_shared_boundary_element_face_index(
21867 nproc);
21868
21869 // Compute any additional shared boundaries by checking the
21870 // intersection between the received elements from each processor
21871 // and the elements just sent to that processor, the lowest
21872 // processors number loops over its received elements and the
21873 // highest loops over its sent elements (halo elements that have
21874 // become part of the domain now can create shared boundaries with
21875 // other processor)
21876
21877 // Note: These additional shared boundaries may be created by the
21878 // elements that previously were halo but now have become part of
21879 // the processor (the received elements), and the elements that were
21880 // previously part of the processor but now have become halo (a
21881 // subset of the sent-elements)
21882
21883 // Then these new shared boundaries come from the intersection of
21884 // the new-haloed elements (received elements) and the new-halo
21885 // elements (sent elements). These could be computed previously (in
21886 // the computing of the local new-halo and local new-haloed elements
21887 // usign the info. of the new domains for the old halo elements),
21888 // however, it was decided to perform the computation here in order to
21889 // avoid the identification of the old halo element that was part of a
21890 // shared boundary in the set of just received elements
21891 for (unsigned iproc = 0; iproc < nproc; iproc++)
21892 {
21893 if (my_rank < iproc)
21894 {
21895 // Lowest processor loops over the received elements
21896 this->get_shared_boundary_elements_and_face_indexes(
21897 received_elements_pt[iproc],
21898 elements_to_send_pt[iproc],
21899 tmp_group1_shared_boundary_element_pt[iproc],
21900 tmp_group1_shared_boundary_element_face_index[iproc],
21901 tmp_group2_shared_boundary_element_pt[iproc],
21902 tmp_group2_shared_boundary_element_face_index[iproc]);
21903
21904 } // if (my_rank < iproc)
21905 else if (my_rank > iproc)
21906 {
21907 // Highest processor loops over the sent elements
21908 this->get_shared_boundary_elements_and_face_indexes(
21909 elements_to_send_pt[iproc],
21910 received_elements_pt[iproc],
21911 tmp_group1_shared_boundary_element_pt[iproc],
21912 tmp_group1_shared_boundary_element_face_index[iproc],
21913 tmp_group2_shared_boundary_element_pt[iproc],
21914 tmp_group2_shared_boundary_element_face_index[iproc]);
21915
21916 } // else if (my_rank > iproc)
21917
21918 } // for (iproc < nproc)
21919
21920 // The time to compute any additional shared boundary
21921 if (Print_timings_level_load_balance > 1)
21922 {
21924 << "CPU for computing additional shared boundaries (load balance) [7]: "
21926 tt_start_compute_additional_shared_boundaries
21927 << std::endl;
21928 }
21929
21930 // =====================================================================
21931 // END: GET ANY ADDITIONAL SHARED BOUNDARY BY THE INTERSECTION OF
21932 // THE ELEMENTS SENT TO PROCESSOR "IPROC" AND THE ELEMENTS RECEIVED
21933 // FROM PROCESSOR "IPROC". IF ANY NEW SHARED BOUNDARY IS FOUND, IT
21934 // IS CREATED BY THE OLD HALO ELEMENTS (RECEIVED ELEMENTS) THAT HAVE
21935 // NOW BECOME PART OF THE DOMAIN AND THE OLD HALOED ELEMENTS (SENT
21936 // ELEMENTS)
21937 // =====================================================================
21938
21939 // =====================================================================
21940 // BEGIN: SORT THE SHARED BOUNDARIES SO THAT THEY ARE CREATED IN THE
21941 // SAME ORDER IN THE INVOLVED PROCESSORS (A PAIR OF PROCESSORS)
21942 // =====================================================================
21943
21944 // Get the time to sort shared boundaries
21945 double tt_start_sort_shared_boundaries = 0.0;
21946 if (Print_timings_level_load_balance > 1)
21947 {
21948 tt_start_sort_shared_boundaries = TimingHelpers::timer();
21949 }
21950
21951 // Once computed the elements that create the shared boundaries,
21952 // sort them so that the shared boundaries are created at the same
21953 // order in both processors that define the shared boundary
21954
21955 // The order is like this
21956
21957 // Lowest processors
21958 // 1) Shared boundary elements received from processors (local in
21959 // other processors)
21960 // 2) Local shared boundary elements (do not include halo elements)
21961 // 3) Shared boundary elements by intersection (already sorted)
21962
21963 // Highest processors
21964 // 1) Local shared boundary elements (do not include halo elements)
21965 // 2) Shared boundary elements received from processors (local in
21966 // other processors)
21967 // 3) Shared boundary elements by intersection (already sorted)
21968
21969 Vector<Vector<FiniteElement*>> new_shared_boundary_element_pt(nproc);
21970 Vector<Vector<unsigned>> new_shared_boundary_element_face_index(nproc);
21971 for (unsigned iproc = 0; iproc < nproc; iproc++)
21972 {
21973 // Lower processor
21974 if (my_rank < iproc)
21975 {
21976 // Copy the elements received from processor "jproc" but that
21977 // are haloed with "iproc" processor
21978 for (unsigned jproc = 0; jproc < nproc; jproc++)
21979 {
21980 // Can not receive elements from itself
21981 if (jproc != my_rank)
21982 {
21983 // Get the number of elements to copy from received processors
21984 const unsigned nrecvd_haloed_shared_bound_ele_jproc_iproc =
21985 new_received_haloed_shared_boundary_element_pt[jproc][iproc]
21986 .size();
21987 for (unsigned e = 0; e < nrecvd_haloed_shared_bound_ele_jproc_iproc;
21988 e++)
21989 {
21990 // Get the element
21991 FiniteElement* ele_pt =
21992 new_received_haloed_shared_boundary_element_pt[jproc][iproc][e];
21993 // Get the face index
21994 const unsigned face_index =
21995 new_received_haloed_shared_boundary_element_face_index[jproc]
21996 [iproc]
21997 [e];
21998
21999 // Add the elements to the containers
22000 new_shared_boundary_element_pt[iproc].push_back(ele_pt);
22001 new_shared_boundary_element_face_index[iproc].push_back(
22002 face_index);
22003
22004 } // for (e < nrecvd_haloed_shared_bound_ele_iproc_jproc)
22005
22006 } // if (jproc != my_rank)
22007
22008 } // for (jproc < nproc)
22009
22010 // Then the local shared haloed (invert the indexes to get the
22011 // haloed elements)
22012 const unsigned nlocal_haloed_shared_bound_ele_iproc_my_rank =
22013 new_local_halo_shared_boundary_element_pt[iproc][my_rank].size();
22014 for (unsigned e = 0; e < nlocal_haloed_shared_bound_ele_iproc_my_rank;
22015 e++)
22016 {
22017 // Get the element
22018 FiniteElement* ele_pt =
22019 new_local_halo_shared_boundary_element_pt[iproc][my_rank][e];
22020 // Get the face index
22021 const unsigned face_index =
22022 new_local_halo_shared_boundary_element_face_index[iproc][my_rank]
22023 [e];
22024
22025 // Only include the element if it is nonhalo (this may be an
22026 // old halo element that helped to indentify a shared boundary
22027 // with iproc)
22028 if (!ele_pt->is_halo())
22029 {
22030 // Add the elements to the containers
22031 new_shared_boundary_element_pt[iproc].push_back(ele_pt);
22032 new_shared_boundary_element_face_index[iproc].push_back(face_index);
22033 } // if (!ele_pt->is_halo())
22034
22035 } // for (e < nlocal_haloed_shared_bound_ele_iproc_my_rank)
22036
22037 // ... and finally any additional shared boundary elements from
22038 // tmp_group1
22039 const unsigned ntmp_group1_shared_bound_ele_iproc =
22040 tmp_group1_shared_boundary_element_pt[iproc].size();
22041 for (unsigned e = 0; e < ntmp_group1_shared_bound_ele_iproc; e++)
22042 {
22043 // Get the element
22044 FiniteElement* ele_pt =
22045 tmp_group1_shared_boundary_element_pt[iproc][e];
22046 // Get the face index
22047 const unsigned face_index =
22048 tmp_group1_shared_boundary_element_face_index[iproc][e];
22049
22050 // Add the elements to the containers
22051 new_shared_boundary_element_pt[iproc].push_back(ele_pt);
22052 new_shared_boundary_element_face_index[iproc].push_back(face_index);
22053
22054 } // for (e < ntmp_group1_shared_bound_ele_iproc)
22055
22056 } // if (my_rank < iproc)
22057 // Highest processor
22058 else if (my_rank > iproc)
22059 {
22060 // Get the haloed elements first and then the elements received
22061 // from processor "jproc" but that are haloed with "iproc"
22062 // processor
22063
22064 // Get the number of elements to copy from local elements
22065 // (invert the indexes to get the haloed elements)
22066 const unsigned nlocal_haloed_shared_bound_ele_iproc_my_rank =
22067 new_local_halo_shared_boundary_element_pt[iproc][my_rank].size();
22068 for (unsigned e = 0; e < nlocal_haloed_shared_bound_ele_iproc_my_rank;
22069 e++)
22070 {
22071 // Get the element
22072 FiniteElement* ele_pt =
22073 new_local_halo_shared_boundary_element_pt[iproc][my_rank][e];
22074 // Get the face index
22075 const unsigned face_index =
22076 new_local_halo_shared_boundary_element_face_index[iproc][my_rank]
22077 [e];
22078
22079 // Only include the element if it is nonhalo (this may be an
22080 // old halo element that helped to indentify a shared boundary
22081 // with iproc)
22082 if (!ele_pt->is_halo())
22083 {
22084 // Add the elements to the containers
22085 new_shared_boundary_element_pt[iproc].push_back(ele_pt);
22086 new_shared_boundary_element_face_index[iproc].push_back(face_index);
22087 } // if (!ele_pt->is_halo())
22088
22089 } // for (e < nlocal_haloed_shared_bound_ele_iproc_my_rank)
22090
22091 for (unsigned jproc = 0; jproc < nproc; jproc++)
22092 {
22093 // Can not receive elements from itself
22094 if (jproc != my_rank)
22095 {
22096 // Then the received shared elements from "jproc" but haloed
22097 // with "iproc"
22098 const unsigned nrecvd_haloed_shared_bound_ele_jproc_iproc =
22099 new_received_haloed_shared_boundary_element_pt[jproc][iproc]
22100 .size();
22101 for (unsigned e = 0; e < nrecvd_haloed_shared_bound_ele_jproc_iproc;
22102 e++)
22103 {
22104 // Get the element
22105 FiniteElement* ele_pt =
22106 new_received_haloed_shared_boundary_element_pt[jproc][iproc][e];
22107 // Get the face index
22108 const unsigned face_index =
22109 new_received_haloed_shared_boundary_element_face_index[jproc]
22110 [iproc]
22111 [e];
22112
22113 // Add the elements to the containers
22114 new_shared_boundary_element_pt[iproc].push_back(ele_pt);
22115 new_shared_boundary_element_face_index[iproc].push_back(
22116 face_index);
22117
22118 } // for (e < nrecvd_haloed_shared_bound_ele_iproc)
22119
22120 } // if (jproc != my_rank)
22121
22122 } // for (jproc < nproc)
22123
22124 // ... and finally any additional shared boundary elements from
22125 // tmp_group2
22126 const unsigned ntmp_group2_shared_bound_ele_iproc =
22127 tmp_group2_shared_boundary_element_pt[iproc].size();
22128 for (unsigned e = 0; e < ntmp_group2_shared_bound_ele_iproc; e++)
22129 {
22130 // Get the element
22131 FiniteElement* ele_pt =
22132 tmp_group2_shared_boundary_element_pt[iproc][e];
22133 // Get the face index
22134 const unsigned face_index =
22135 tmp_group2_shared_boundary_element_face_index[iproc][e];
22136
22137 // Add the elements to the containers
22138 new_shared_boundary_element_pt[iproc].push_back(ele_pt);
22139 new_shared_boundary_element_face_index[iproc].push_back(face_index);
22140
22141 } // for (e < ntmp_group2_shared_bound_ele_iproc)
22142
22143 } // else if (my_rank > iproc)
22144
22145 } // for (iproc < nproc)
22146
22147 // The time to sort shared boundaries
22148 if (Print_timings_level_load_balance > 1)
22149 {
22150 oomph_info << "CPU for sorting shared boundaries (load balance) [8]: "
22151 << TimingHelpers::timer() - tt_start_sort_shared_boundaries
22152 << std::endl;
22153 }
22154
22155 // =====================================================================
22156 // END: SORT THE SHARED BOUNDARIES SO THAT THEY ARE CREATED IN THE
22157 // SAME ORDER IN THE INVOLVED PROCESSORS (A PAIR OF PROCESSORS)
22158 // =====================================================================
22159
22160 // =====================================================================
22161 // BEGIN: CREATE THE NEW SHARED BOUNDARIES. BEFORE THE GENERATION OF
22162 // THE SHARED BOUNDARIES PUT IN A CONTAINER THOSE NONHALO ELEMENTS
22163 // THAT WILL REMAIN IN THE CURRENT PROCESSOR (BECAUSE THEIR RANK IS
22164 // THE SAME AS THE CURRENT PROCESSOR), AND THOSE ELEMENTS RECEIVED
22165 // FROM OTHER PROCESSORS. THESE SET OF ELEMENTS WILL BE USED TO
22166 // CHECK FOR POSSIBLE CONNECTIONS OF THE NEW SHARED BOUNDARIES WITH
22167 // THE ORIGINAL BOUNDARIES
22168 // =====================================================================
22169 // Finally, create the new shared boundaries
22170
22171 // Get the time to create the new shared boundaries
22172 double tt_start_create_new_shared_boundaries = 0.0;
22173 if (Print_timings_level_load_balance > 1)
22174 {
22175 tt_start_create_new_shared_boundaries = TimingHelpers::timer();
22176 }
22177
22178 // Compute the elements that will remain after deletion in the
22179 // curent processor. This is required to check if the new shared
22180 // boundaries crete a connection with any node of the elements in
22181 // the boundaries
22182
22183 // Try to use as much information as possible
22184
22185 // Storage for the elements in the processor
22186 std::set<FiniteElement*> element_in_processor_pt;
22187
22188 // Loop over the old elements, those before sending/received
22189 // elements to/from other processors
22190 unsigned nh_count6 = 0;
22191 for (unsigned e = 0; e < nelement_before_load_balance; e++)
22192 {
22193 // Get the element
22194 FiniteElement* ele_pt = backed_up_ele_pt[e];
22195 // Only work with nonhalo elements
22196 if (!(ele_pt->is_halo()))
22197 {
22198 // Is the element part of the new domain
22199 if (target_domain_for_local_non_halo_element[nh_count6++] == my_rank)
22200 {
22201 // Add the element to the set of elements in the processor
22202 element_in_processor_pt.insert(ele_pt);
22203 }
22204
22205 } // if (!(ele_pt->is_halo()))
22206
22207 } // for (e < nelement_before_load_balance)
22208
22209 // Now include the received elements from the other processors
22210 // Loop over the processors
22211 for (unsigned iproc = 0; iproc < nproc; iproc++)
22212 {
22213 // No elements received from myself
22214 if (iproc != my_rank)
22215 {
22216 // Get the number of received elements with the "iproc"
22217 // processor
22218 const unsigned n_received_ele = received_elements_pt[iproc].size();
22219 for (unsigned ie = 0; ie < n_received_ele; ie++)
22220 {
22221 // Get the ie-th received element from processor iproc
22222 FiniteElement* ele_pt = received_elements_pt[iproc][ie];
22223
22224 // Include it in the set of elements in the processor
22225 element_in_processor_pt.insert(ele_pt);
22226
22227 } // for (ie < nreceived_ele)
22228
22229 } // if (iproc != my_rank)
22230
22231 } // for (iproc < nproc)
22232
22233 // Now create the shared boundaries
22234 create_new_shared_boundaries(element_in_processor_pt,
22235 new_shared_boundary_element_pt,
22236 new_shared_boundary_element_face_index);
22237
22238 // The time to create the new shared boundaries
22239 if (Print_timings_level_load_balance > 1)
22240 {
22242 << "CPU for creating new shared boundaries (load balance) [9]: "
22243 << TimingHelpers::timer() - tt_start_create_new_shared_boundaries
22244 << std::endl;
22245 }
22246
22247 // =====================================================================
22248 // END: CREATE THE NEW SHARED BOUNDARIES. BEFORE THE GENERATION OF
22249 // THE SHARED BOUNDARIES PUT IN A CONTAINER THOSE NONHALO ELEMENTS
22250 // THAT WILL REMAIN IN THE CURRENT PROCESSOR (BECAUSE THEIR RANK IS
22251 // THE SAME AS THE CURRENT PROCESSOR), AND THOSE ELEMENTS RECEIVED
22252 // FROM OTHER PROCESSORS. THESE SET OF ELEMENTS WILL BE USED TO
22253 // CHECK FOR POSSIBLE CONNECTIONS OF THE NEW SHARED BOUNDARIES WITH
22254 // THE ORIGINAL BOUNDARIES
22255 // =====================================================================
22256
22257 // =====================================================================
22258 // BEGIN: DELETE THE ELEMENTS NO LONGER BELONGING TO THE DOMAIN,
22259 // INCLUDING HALO ELEMENTS. ADD THE KEPT ELEMENTS TO THE MESH AND
22260 // THE RECEIVED ELEMENTS FROM OTHER PROCESSORS
22261 // =====================================================================
22262
22263 // Get the time to delete elements no longer belonging to the
22264 // processor
22265 double tt_start_delete_elements = 0.0;
22266 if (Print_timings_level_load_balance > 1)
22267 {
22268 tt_start_delete_elements = TimingHelpers::timer();
22269 }
22270
22271 // Once computed the new shared boundaries delete the elements that
22272 // no longer belong to the processor (including the old halo
22273 // elements)
22274
22275 // The procedure is similar to the one performed at the distribution
22276 // stage (src/generic/mesh.cc -- distribute() method)
22277
22278 // Clean the storage for halo(ed) elements/nodes
22279 this->Halo_node_pt.clear();
22280 this->Root_halo_element_pt.clear();
22281
22282 this->Haloed_node_pt.clear();
22283 this->Root_haloed_element_pt.clear();
22284
22285 // Mark all the nodes as obsolete
22286 const unsigned nnodes = this->nnode();
22287 for (unsigned j = 0; j < nnodes; j++)
22288 {
22289 this->node_pt(j)->set_obsolete();
22290 }
22291
22292 // Flush the mesh storage
22293 this->flush_element_storage();
22294
22295 // Delete any storage of external elements and nodes
22296 this->delete_all_external_storage();
22297
22298 // Clear external storage
22299 this->External_halo_node_pt.clear();
22300 this->External_halo_element_pt.clear();
22301
22302 this->External_haloed_node_pt.clear();
22303 this->External_haloed_element_pt.clear();
22304
22305 // Keep track of the deleted elements
22306 Vector<FiniteElement*> deleted_elements;
22307
22308 // Delete the elements that no longer belong to the processor
22309 unsigned nh_count7 = 0;
22310 for (unsigned e = 0; e < nelement_before_load_balance; e++)
22311 {
22312 FiniteElement* ele_pt = backed_up_ele_pt[e];
22313 // Only work with nonhalo elements
22314 if (!(ele_pt->is_halo()))
22315 {
22316 if (target_domain_for_local_non_halo_element[nh_count7++] == my_rank)
22317 {
22318 // Add the element to the mesh
22319 this->add_element_pt(ele_pt);
22320 // Get the number of nodes on the element
22321 const unsigned nele_nodes = ele_pt->nnode();
22322 // Loop over the nodes of the element
22323 for (unsigned j = 0; j < nele_nodes; j++)
22324 {
22325 // Mark the node as non-obsolete
22326 ele_pt->node_pt(j)->set_non_obsolete();
22327 } // for (j < nele_nodes)
22328
22329 } // The element belongs to the domain
22330 else
22331 {
22332 // Delete the element, but keep track of it
22333 deleted_elements.push_back(ele_pt);
22334 // Delete and point to null
22335 delete ele_pt;
22336 ele_pt = 0;
22337 }
22338
22339 } // if (!(ele_pt->is_halo()))
22340 else
22341 {
22342 // If the element is halo, delete if but keep track of it
22343 deleted_elements.push_back(ele_pt);
22344 // Delete and point to null
22345 delete ele_pt;
22346 ele_pt = 0;
22347 }
22348
22349 } // for (e < nelement_before_load_balance)
22350
22351 // Now add the received elements from each processor
22352 for (unsigned iproc = 0; iproc < nproc; iproc++)
22353 {
22354 if (iproc != my_rank)
22355 {
22356 // Get the number of received elements with the "iproc"
22357 // processor
22358 const unsigned nreceived_ele = received_elements_pt[iproc].size();
22359 for (unsigned ie = 0; ie < nreceived_ele; ie++)
22360 {
22361 // Get the element and add it to the mesh
22362 FiniteElement* ele_pt = received_elements_pt[iproc][ie];
22363 // Add the element to the mesh
22364 this->add_element_pt(ele_pt);
22365 // Get the number of nodes on the element
22366 const unsigned nele_nodes = ele_pt->nnode();
22367 // Loop over the nodes of the element
22368 for (unsigned j = 0; j < nele_nodes; j++)
22369 {
22370 // Mark the node as non-obsolete
22371 ele_pt->node_pt(j)->set_non_obsolete();
22372 } // for (j < nele_nodes)
22373
22374 } // for (ie < nreceived_ele)
22375
22376 } // if (iproc != my_rank)
22377
22378 } // for (iproc < nproc)
22379
22380 // Now remove the obsolete nodes
22381 this->prune_dead_nodes();
22382
22383 // The time to delete elements no longer belonging to the processor
22384 if (Print_timings_level_load_balance > 1)
22385 {
22386 oomph_info << "CPU for deleting elements no longer belonging to this "
22387 "processor (load balance) [10]: "
22388 << TimingHelpers::timer() - tt_start_delete_elements
22389 << std::endl;
22390 }
22391
22392 // =====================================================================
22393 // END: DELETE THE ELEMENTS NO LONGER BELONGING TO THE DOMAIN,
22394 // INCLUDING HALO ELEMENTS. ADD THE KEPT ELEMENTS TO THE MESH AND
22395 // THE RECEIVED ELEMENTS FROM OTHER PROCESSORS
22396 // =====================================================================
22397
22398 // =====================================================================
22399 // BEGIN: REESTABLISH THE HALO(ED) SCHEME, ATTACH HALO ELEMENTS
22400 // (HALO NODES INCLUDED) TO THE NEW MESH (AFTER LOAD BALANCING)
22401 // RESTORE THE BOUNDARY ELEMENTS SCHEME AND THE NUMBER OF SEGMENTS
22402 // ON EACH BOUNDARY
22403 // =====================================================================
22404
22405 // Get the time to re-establish the halo(ed) information
22406 double tt_start_re_etablish_halo_ed_info = 0.0;
22407 if (Print_timings_level_load_balance > 1)
22408 {
22409 tt_start_re_etablish_halo_ed_info = TimingHelpers::timer();
22410 }
22411
22412 // Prepare the data to re-establish the halo(ed) scheme
22413
22414 // Sort the nodes on the new shared boundaries so that they have the
22415 // same order on all processors
22416 this->sort_nodes_on_shared_boundaries();
22417
22418 // Before re-establish the halo and haloed elements save the number
22419 // of current elements in the boundaries, this will be useful to
22420 // re-establish the boundary elements. Notice that there may be
22421 // boundary elements with null pointers, since the element may no
22422 // longer belong to the current processor
22423 const unsigned tmp_nboundary = this->nboundary();
22424 Vector<unsigned> ntmp_boundary_elements(tmp_nboundary);
22425
22426 // If there are regions, save the number of boundary-region elements
22427 Vector<Vector<unsigned>> ntmp_boundary_elements_in_region(tmp_nboundary);
22428 // Are there regions?
22429 const unsigned n_regions = this->nregion();
22430
22431 // Loop over the boundaries
22432 for (unsigned ib = 0; ib < tmp_nboundary; ib++)
22433 {
22434 // Get the number of boundary elements
22435 ntmp_boundary_elements[ib] = this->nboundary_element(ib);
22436
22437 // Resize the container
22438 ntmp_boundary_elements_in_region[ib].resize(n_regions);
22439
22440 // Loop over the regions
22441 for (unsigned rr = 0; rr < n_regions; rr++)
22442 {
22443 // Get the region id
22444 const unsigned region_id =
22445 static_cast<unsigned>(this->region_attribute(rr));
22446
22447 // Store the number of element in the region (notice we are
22448 // using the region index not the region id to refer to the
22449 // region)
22450 ntmp_boundary_elements_in_region[ib][rr] =
22451 this->nboundary_element_in_region(ib, region_id);
22452
22453 } // for (rr < n_regions)
22454
22455 } // for (ib < tmp_nboundary)
22456
22457 // Re-establish the halo(ed) scheme
22458 this->reset_halo_haloed_scheme();
22459
22460 // Get the number of elements in the mesh after load balance
22461 const unsigned nelement_after_load_balance = this->nelement();
22462
22463 // We need to reset boundary elements because we need to get rid of
22464 // the old boundary elements and stay only with the new ones
22465 this->reset_boundary_element_info(ntmp_boundary_elements,
22466 ntmp_boundary_elements_in_region,
22467 deleted_elements);
22468
22469 // There is no need to re-set boundary coordinates since the
22470 // load-balanced mesh already has the correct information (the
22471 // boundary coordinate for each node was sent with the node
22472 // information)
22473
22474 // We need to re-compute the number of segments on each boundary
22475 // after load balance. It may be possible that the boundary is now
22476 // split in more segments, or that previous gaps between the
22477 // segments have now dissapeared because the received elements
22478 // filled those gaps
22479
22480 // In order to re-set the number of segments it is required to get
22481 // the face elements, attach them to create a contiguous
22482 // representation of the boundary (in segments possibly) and then
22483 // counter the number of segments. This can only be done after
22484 // restoring the boundary elements scheme (which has been done
22485 // above)
22486
22487 // Set the number of segments for the boundaries with geom objects
22488 // associated. The correct value is not on the original mesh since
22489 // it is computed only when calling then
22490 // setup_boundary_coordinates() method (called only for those
22491 // boundaries with no geom object associated)
22492 for (unsigned b = 0; b < tmp_nboundary; b++)
22493 {
22494 if (this->boundary_geom_object_pt(b) != 0)
22495 {
22496 // Clear the boundary segment nodes storage
22497 this->flush_boundary_segment_node(b);
22498
22499 // Dummy vector of nodes on segments
22500 Vector<Vector<Node*>> dummy_segment_node_pt;
22501
22502 // Compute the new number of segments in the boundary
22503 get_boundary_segment_nodes_helper(b, dummy_segment_node_pt);
22504
22505 // Get the number of segments from the vector of nodes
22506 const unsigned nsegments = dummy_segment_node_pt.size();
22507
22508 // Set the number of segments for the storing of the nodes
22509 // associated to the segments
22510 this->set_nboundary_segment_node(b, nsegments);
22511 } // if (this->boundary_geom_object_pt(b)!=0)
22512
22513 } // for (b < n_boundary)
22514
22515 // The time to re-establish the halo(ed) information
22516 if (Print_timings_level_load_balance > 1)
22517 {
22519 << "CPU for re-establishing halo(ed) information (load balance) [11]: "
22520 << TimingHelpers::timer() - tt_start_re_etablish_halo_ed_info
22521 << std::endl;
22522 }
22523
22524 // =====================================================================
22525 // END: REESTABLISH THE HALO(ED) SCHEME, ATTACH HALO ELEMENTS (HALO
22526 // NODES INCLUDED) TO THE NEW MESH (AFTER LOAD BALANCING) RESTORE
22527 // THE BOUNDARY ELEMENTS SCHEME AND THE NUMBER OF SEGMENTS ON EACH
22528 // BOUNDARY
22529 // =====================================================================
22530
22531 if (Print_timings_level_load_balance > 1)
22532 {
22533 oomph_info << "CPU for load balance [n_ele_before="
22534 << nelement_before_load_balance
22535 << ", n_ele_after=" << nelement_after_load_balance << "]: "
22536 << TimingHelpers::timer() - t_start_overall_load_balance
22537 << std::endl;
22538 }
22539
22540 oomph_info << "Load balance (unstructured mesh) [END]" << std::endl;
22541 }
22542
22543 //======================================================================
22544 /// Use the first and second group of elements to find the
22545 /// intersection between them to get the shared boundary
22546 /// elements from the first and second group
22547 //======================================================================
22548 template<class ELEMENT>
22551 const Vector<FiniteElement*>& first_element_pt,
22552 const Vector<FiniteElement*>& second_element_pt,
22553 Vector<FiniteElement*>& first_shared_boundary_element_pt,
22554 Vector<unsigned>& first_shared_boundary_element_face_index,
22555 Vector<FiniteElement*>& second_shared_boundary_element_pt,
22556 Vector<unsigned>& second_shared_boundary_element_face_index)
22557 {
22558 // 1) Compare their faces (nodes) and if they match then they are
22559 // part of a shared boundary
22560 // 2) Save the first and second group of elements that give rise to
22561 // the shared boundary, also include the face index
22562
22563 // Get the number of elements on the first group
22564 const unsigned nfirst_element = first_element_pt.size();
22565 // Loop over the elements in the first group
22566 for (unsigned ef = 0; ef < nfirst_element; ef++)
22567 {
22568 // Get the element
22569 FiniteElement* fele_pt = first_element_pt[ef];
22570 // Check if the element is halo
22571 bool first_ele_is_halo = false;
22572 if (fele_pt->is_halo())
22573 {
22574 first_ele_is_halo = true;
22575 }
22576 // Get each of the faces
22577 for (unsigned ifface = 0; ifface < 3; ifface++)
22578 {
22579 Vector<Node*> first_face(2);
22580 if (ifface == 0)
22581 {
22582 first_face[0] = fele_pt->node_pt(1);
22583 first_face[1] = fele_pt->node_pt(2);
22584 }
22585 else if (ifface == 1)
22586 {
22587 first_face[0] = fele_pt->node_pt(2);
22588 first_face[1] = fele_pt->node_pt(0);
22589 }
22590 else if (ifface == 2)
22591 {
22592 first_face[0] = fele_pt->node_pt(0);
22593 first_face[1] = fele_pt->node_pt(1);
22594 }
22595
22596 // Now check each of the faces with the faces on the second
22597 // elements
22598
22599 // Get the number of elements on the second group
22600 const unsigned nsecond_element = second_element_pt.size();
22601 // Loop over the elements in the second group
22602 for (unsigned es = 0; es < nsecond_element; es++)
22603 {
22604 // Get the element
22605 FiniteElement* sele_pt = second_element_pt[es];
22606 // Check if the element is halo
22607 bool second_ele_is_halo = false;
22608 if (sele_pt->is_halo())
22609 {
22610 second_ele_is_halo = true;
22611 }
22612 // Now check whether both elements are halo, if that is the
22613 // case then we go for the next elements. We can not look for
22614 // shared boundaries between halo elements since other
22615 // processors, those with the nonhalo counterpart of the
22616 // elements, are in charge of creating those shared boundaries
22617 if (!(first_ele_is_halo && second_ele_is_halo))
22618 {
22619 // Get each of the faces
22620 for (unsigned isface = 0; isface < 3; isface++)
22621 {
22622 Vector<Node*> second_face(2);
22623 if (isface == 0)
22624 {
22625 second_face[0] = sele_pt->node_pt(1);
22626 second_face[1] = sele_pt->node_pt(2);
22627 }
22628 else if (isface == 1)
22629 {
22630 second_face[0] = sele_pt->node_pt(2);
22631 second_face[1] = sele_pt->node_pt(0);
22632 }
22633 else if (isface == 2)
22634 {
22635 second_face[0] = sele_pt->node_pt(0);
22636 second_face[1] = sele_pt->node_pt(1);
22637 }
22638
22639 // Now check for any intersection among first and second
22640 // faces
22641 if (first_face[0] == second_face[0] &&
22642 first_face[1] == second_face[1])
22643 {
22644 // Save the elements on the corresponding containers
22645 first_shared_boundary_element_pt.push_back(fele_pt);
22646 // .. and the face index
22647 first_shared_boundary_element_face_index.push_back(ifface);
22648
22649 // Save the elements on the corresponding containers
22650 second_shared_boundary_element_pt.push_back(sele_pt);
22651 // .. and the face index
22652 second_shared_boundary_element_face_index.push_back(isface);
22653
22654 // Break the loop over the faces of the first elements
22655 // and the first elements, we need to continue looking
22656 // on the next face of the first elements
22657
22658 // Increase the indexes to force breaking the loop
22659 isface = 3;
22660 es = nsecond_element;
22661 }
22662 // Check for intersection with the reversed case too
22663 else if (first_face[0] == second_face[1] &&
22664 first_face[1] == second_face[0])
22665 {
22666 // Save the elements on the corresponding containers
22667 first_shared_boundary_element_pt.push_back(fele_pt);
22668 // .. and the face index
22669 first_shared_boundary_element_face_index.push_back(ifface);
22670
22671 // Save the elements on the corresponding containers
22672 second_shared_boundary_element_pt.push_back(sele_pt);
22673 // .. and the face index
22674 second_shared_boundary_element_face_index.push_back(isface);
22675
22676 // Break the loop over the faces of the first elements
22677 // and the first elements, we need to continue looking
22678 // on the next face of the first elements
22679
22680 // Increase the indexes to force breaking the loop
22681 isface = 3;
22682 es = nsecond_element;
22683 }
22684
22685 } // for (isface < 3)
22686
22687 } // if (!(first_ele_is_halo && second_ele_is_halo))
22688
22689 } // for (es < nsecond_element)
22690
22691 } // for (ifface < 3)
22692
22693 } // for (ef < nfirst_element)
22694 }
22695
22696 //======================================================================
22697 /// Creates the new shared boundaries, this method is also in
22698 /// charge of computing the shared boundaries ids of each processor
22699 /// and send that info. to all the processors
22700 //======================================================================
22701 template<class ELEMENT>
22703 std::set<FiniteElement*>& element_in_processor_pt,
22704 Vector<Vector<FiniteElement*>>& new_shared_boundary_element_pt,
22705 Vector<Vector<unsigned>>& new_shared_boundary_element_face_index)
22706 {
22707 // Get the number of processors
22708 const unsigned nproc = this->communicator_pt()->nproc();
22709 // Get the rank of the current processor
22710 const unsigned my_rank = this->communicator_pt()->my_rank();
22711
22712 // ================================================================
22713 // BEGIN: GET THE SHARED BOUNDARY FACE ELEMENTS FROM THE SHARED
22714 // BOUNDARY ELEMENTS, AND ASSIGN A ROOT EDGE TO EACH FACE
22715 // ELEMENT. AVOID THE CREATION OF FACE ELEMENTS THAT REPRESENT THE
22716 // SAME EDGE (INTERNAL BOUNDARIES)
22717 // ================================================================
22718
22719 // Get the time to get edges from shared boundary face elements
22720 double tt_start_get_edges_from_shd_bnd_face_ele = 0.0;
22721 if (Print_timings_level_load_balance > 2)
22722 {
22723 tt_start_get_edges_from_shd_bnd_face_ele = TimingHelpers::timer();
22724 }
22725
22726 // Face elements that create the shared boundaries (unsorted)
22727 Vector<Vector<FiniteElement*>> tmp_unsorted_face_ele_pt(nproc);
22728 // The elements from where the face element was created
22729 Vector<Vector<FiniteElement*>> tmp_unsorted_ele_pt(nproc);
22730 // The face index of the bulk element from where was created the
22731 // face element
22732 Vector<Vector<int>> tmp_unsorted_face_index_ele(nproc);
22733
22734 // Store the current edges lying on boundaries (this will help for
22735 // any edge of a shared boundary lying on an internal boundary)
22736 std::map<std::pair<Node*, Node*>, unsigned> elements_edges_on_boundary;
22737
22738 // Compute the edges on the other boundaries
22739 this->get_element_edges_on_boundary(elements_edges_on_boundary);
22740
22741 // Mark those edges (pair of nodes overlapped by a shared boundary)
22742 std::map<std::pair<Node*, Node*>, bool> overlapped_edge;
22743
22744 // Associate every found edge (face element) on the shared boundary
22745 // with an original boundary only if the edge (face element) lies
22746 // (overlaps) on an original boundary, it may happen only for
22747 // internal boundaries
22748 Vector<Vector<int>> tmp_edge_boundary(nproc);
22749
22750 // Get the face elements from the shared boundary elements with in
22751 // each processor
22752 for (unsigned iproc = 0; iproc < nproc; iproc++)
22753 {
22754 // There are no shared boundary elements with myself
22755 if (iproc != my_rank)
22756 {
22757 // Get the number of shared boundary elements with in "iproc"
22758 // processor
22759 const unsigned n_shared_bound_ele =
22760 new_shared_boundary_element_pt[iproc].size();
22761
22762 // Avoid to create repeated face elements, compare the nodes on
22763 // the edges of the face elements
22765
22766 // Count the number of repeated faces
22767 unsigned nrepeated_faces = 0;
22768
22769 // Loop over the shared boundary elements with the iproc
22770 // processor
22771 for (unsigned iele = 0; iele < n_shared_bound_ele; iele++)
22772 {
22773 // Get the bulk element
22774 FiniteElement* bulk_ele_pt =
22775 new_shared_boundary_element_pt[iproc][iele];
22776
22777 // Get the face index
22778 int face_index = static_cast<int>(
22779 new_shared_boundary_element_face_index[iproc][iele]);
22780
22781 // Create the face element
22782 FiniteElement* tmp_ele_pt =
22783 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
22784
22785 // Before adding the face element to the vector check that is
22786 // not has been previously created
22787 bool done_face = false;
22788
22789 // Get the number of nodes on the face element and get the first
22790 // and last node
22791 const unsigned nnode_face_ele = tmp_ele_pt->nnode();
22792 Node* first_face_node_pt = tmp_ele_pt->node_pt(0);
22793 Node* last_face_node_pt = tmp_ele_pt->node_pt(nnode_face_ele - 1);
22794
22795 // Get the number of already done face elements
22796 const unsigned ndone_faces = done_faces.size();
22797 // Loop over the already visited face elements
22798 for (unsigned n = 0; n < ndone_faces; n++)
22799 {
22800 Node* first_done_face_node_pt = done_faces[n].first;
22801 Node* second_done_face_node_pt = done_faces[n].second;
22802 if (first_face_node_pt == first_done_face_node_pt &&
22803 last_face_node_pt == second_done_face_node_pt)
22804 {
22805 done_face = true;
22806 nrepeated_faces++;
22807 break;
22808 }
22809 // Check for the reversed case
22810 else if (first_face_node_pt == second_done_face_node_pt &&
22811 last_face_node_pt == first_done_face_node_pt)
22812 {
22813 done_face = true;
22814 nrepeated_faces++;
22815 break;
22816 }
22817
22818 } // for (n < ndone_faces)
22819
22820 // Only include the faces that are not repeated
22821 if (!done_face)
22822 {
22823 // Add the face element in the vector
22824 tmp_unsorted_face_ele_pt[iproc].push_back(tmp_ele_pt);
22825 // Add the bulk element to the vector
22826 tmp_unsorted_ele_pt[iproc].push_back(bulk_ele_pt);
22827 // Add the face index to the vector
22828 tmp_unsorted_face_index_ele[iproc].push_back(face_index);
22829 // Include the nodes in the done nodes vector
22830 std::pair<Node*, Node*> tmp_edge =
22831 std::make_pair(first_face_node_pt, last_face_node_pt);
22832 // Push the edge
22833 done_faces.push_back(tmp_edge);
22834
22835 // Associate the face element with a boundary (if that is
22836 // the case)
22837 int edge_boundary_id = -1;
22838 std::map<std::pair<Node*, Node*>, unsigned>::iterator it;
22839 it = elements_edges_on_boundary.find(tmp_edge);
22840 // If the edges lie on a boundary then get the boundary id
22841 // on which the edges lie
22842 if (it != elements_edges_on_boundary.end())
22843 {
22844 // Assign the internal boundary id associated with the
22845 // edge
22846 edge_boundary_id = (*it).second;
22847 // Mark the edge as overlapped
22848 overlapped_edge[tmp_edge] = true;
22849 // Also include the reversed version of the edge
22850 std::pair<Node*, Node*> rev_tmp_edge =
22851 std::make_pair(last_face_node_pt, first_face_node_pt);
22852 // Mark the reversed version of the edge as overlapped
22853 overlapped_edge[rev_tmp_edge] = true;
22854 }
22855 else
22856 {
22857 // Look for the reversed version
22858 std::pair<Node*, Node*> rtmp_edge =
22859 std::make_pair(last_face_node_pt, first_face_node_pt);
22860 it = elements_edges_on_boundary.find(rtmp_edge);
22861 if (it != elements_edges_on_boundary.end())
22862 {
22863 // Assign the internal boundary id associated with the
22864 // edge
22865 edge_boundary_id = (*it).second;
22866 // Mark the edge as overlapped
22867 overlapped_edge[rtmp_edge] = true;
22868 // Mark the reversed version (normal) of the edge as
22869 // overlapped
22870 overlapped_edge[tmp_edge] = true;
22871 }
22872 }
22873 // Associate the edge with a boundary
22874 tmp_edge_boundary[iproc].push_back(edge_boundary_id);
22875 } // if (!done_face)
22876 else
22877 {
22878 // Delete the repeated face elements
22879 delete tmp_ele_pt;
22880 tmp_ele_pt = 0;
22881 }
22882
22883 } // for (iele < n_shared_bound_ele)
22884
22885 } // if (iproc != my_rank)
22886
22887 } // for (iproc < nproc)
22888
22889 // The time to get edges from shared boundary face elements
22890 if (Print_timings_level_load_balance > 2)
22891 {
22892 oomph_info << "CPU for getting edges from shared boundary face elements "
22893 "(load balance) [9.1]: "
22895 tt_start_get_edges_from_shd_bnd_face_ele
22896 << std::endl;
22897 }
22898
22899 // ================================================================
22900 // END: GET THE SHARED BOUNDARY FACE ELEMENTS FROM THE SHARED
22901 // BOUNDARY ELEMENTS, AND ASSIGN A ROOT EDGE TO EACH FACE
22902 // ELEMENT. AVOID THE CREATION OF FACE ELEMENTS THAT REPRESENT THE
22903 // SAME EDGE (INTERNAL BOUNDARIES)
22904 // ================================================================
22905
22906 // ================================================================
22907 // BEGIN: BEFORE SORTING THE SHARED FACE ELEMENTS AND ITS ASSOCIATED
22908 // DATA, WE NEED TO ENSURE THAT THEY APPEAR (OR ARE STORED) IN THE
22909 // SAME ORDER IN BOTH OF THE PROCESSORS THAT CREATED THEM. WE USE
22910 // THE BOTTOM-LEFT NODE OF EACH FACE ELEMENT TO STORE THEM IN THE
22911 // SAME ORDER IN BOTH PROCESSORS. ALSO ENSURE THAT THE FACE ELEMENTS
22912 // AGREE WITH THE FIRST AND LAST NODE IN ALL PROCESSORS
22913 // ================================================================
22914
22915 // Get the time to sort shared face elements
22916 double tt_start_sort_shared_face_elements = 0.0;
22917 if (Print_timings_level_load_balance > 2)
22918 {
22919 tt_start_sort_shared_face_elements = TimingHelpers::timer();
22920 }
22921
22922 // -----------------------------------------------------------------
22923 // Before continuing we need to ensured that the face elements are
22924 // stored in the same order in all processors. Sort them starting
22925 // from the face element with the bottom-left node coordinate
22926
22927 // Face elements that create the shared boundaries (unsorted)
22928 Vector<Vector<FiniteElement*>> unsorted_face_ele_pt(nproc);
22929 // The elements from where the face element was created
22930 Vector<Vector<FiniteElement*>> unsorted_ele_pt(nproc);
22931 // The face index of the bulk element from where was created the
22932 // face element
22933 Vector<Vector<int>> unsorted_face_index_ele(nproc);
22934 // Associate every found edge on the shared boundary with an
22935 // original boundary only if the edge lies on an original boundary,
22936 // it may happen only for internal boundaries
22937 Vector<Vector<int>> edge_boundary(nproc);
22938
22939 // For each face element, mark if the element should be considered
22940 // in its inverted way to fullfill with the bottom-left node to be
22941 // the first (left) node. First get the status of each element and
22942 // when they get sorted copy the values across
22943 std::vector<std::vector<bool>> tmp_treat_as_inverted(nproc);
22944 // Vector to store the status of the sorted face elements based on
22945 // the bottom-left condition
22946 std::vector<std::vector<bool>> treat_as_inverted(nproc);
22947
22948 // Get the bottom-left node of each face element and sort them
22949 // starting from the face element with the bottom-left node
22950
22951 // Loop over the processors
22952 for (unsigned iproc = 0; iproc < nproc; iproc++)
22953 {
22954 // There are no shared face elements with myself
22955 if (iproc != my_rank)
22956 {
22957 // Get the number of unsorted face elements
22958 const unsigned n_face_ele = tmp_unsorted_face_ele_pt[iproc].size();
22959 // Store the centroid of the face element. Perform the sorting
22960 // based on the bottom-left centroid of each face element
22961 Vector<Vector<double>> centroid_vertices(n_face_ele);
22962
22963 // Resize the storage for the treating as inverted face element
22964 // storage
22965 tmp_treat_as_inverted[iproc].resize(n_face_ele);
22966
22967 // Loop over the face elements associated with the iproc
22968 // processor
22969 for (unsigned e = 0; e < n_face_ele; e++)
22970 {
22971 // Get the face element
22972 FiniteElement* face_ele_pt = tmp_unsorted_face_ele_pt[iproc][e];
22973 // Get the number of nodes of the face element
22974 const unsigned n_node = face_ele_pt->nnode();
22975 Vector<double> bottom_left(2);
22976 // Assign as the bottom-left node the first node
22977 // Get the node
22978 Node* node_pt = face_ele_pt->node_pt(0);
22979 bottom_left[0] = node_pt->x(0);
22980 bottom_left[1] = node_pt->x(1);
22981 // Set as not treat as inverted element
22982 tmp_treat_as_inverted[iproc][e] = false;
22983 // Loop over the nodes to get the bottom-left vertex of all
22984 // the nodes
22985 for (unsigned n = 1; n < n_node; n++)
22986 {
22987 // Get the node
22988 Node* node_pt = face_ele_pt->node_pt(n);
22989 if (node_pt->x(1) < bottom_left[1])
22990 {
22991 bottom_left[0] = node_pt->x(0);
22992 bottom_left[1] = node_pt->x(1);
22993 // The first node is no longer the bottom-left node, we
22994 // need to treat the element as inverted
22995 tmp_treat_as_inverted[iproc][e] = true;
22996 } // if (node_pt->x(1) < bottom_left[1])
22997 else if (node_pt->x(1) == bottom_left[1])
22998 {
22999 if (node_pt->x(0) < bottom_left[0])
23000 {
23001 bottom_left[0] = node_pt->x(0);
23002 bottom_left[1] = node_pt->x(1);
23003 // The first node is no longer the bottom-left node, we
23004 // need to treat the element as inverted
23005 tmp_treat_as_inverted[iproc][e] = true;
23006 } // if (node_pt->x(0) < bottom_left[0])
23007 } // else if (node_pt->x(1) == bottom_left[1])
23008
23009 } // for (n < n_node
23010
23011 // Resize the container
23012 centroid_vertices[e].resize(2);
23013 // Add the centroid of the face element
23014 centroid_vertices[e][0] = (face_ele_pt->node_pt(0)->x(0) +
23015 face_ele_pt->node_pt(n_node - 1)->x(0)) *
23016 0.5;
23017 centroid_vertices[e][1] = (face_ele_pt->node_pt(0)->x(1) +
23018 face_ele_pt->node_pt(n_node - 1)->x(1)) *
23019 0.5;
23020
23021 } // for (e < n_face_ele)
23022
23023 // Sort the face elements based on their bottom-left node
23024 unsigned n_sorted_bottom_left = 0;
23025 // Keep track of the already sorted face elements
23026 std::vector<bool> done_face(n_face_ele, false);
23027
23028 // Loop until all face elements have been sorted
23029 while (n_sorted_bottom_left < n_face_ele)
23030 {
23031 // The index of the next bottom-left face element
23032 unsigned index = 0;
23033 Vector<double> current_bottom_left(2);
23034 for (unsigned e = 0; e < n_face_ele; e++)
23035 {
23036 // Get the first not done face element
23037 if (!done_face[e])
23038 {
23039 // Store the first not done
23040 current_bottom_left[0] = centroid_vertices[e][0];
23041 current_bottom_left[1] = centroid_vertices[e][1];
23042 // Set the index
23043 index = e;
23044 // Break
23045 break;
23046 } // if (!done_face[e])
23047
23048 } // for (e < n_face_ele)
23049
23050 // Loop over all the other nondone face elements
23051 for (unsigned e = index + 1; e < n_face_ele; e++)
23052 {
23053 // Get the first not done face element
23054 if (!done_face[e])
23055 {
23056 if (centroid_vertices[e][1] < current_bottom_left[1])
23057 {
23058 // Re-set the current bottom left vertex
23059 current_bottom_left[0] = centroid_vertices[e][0];
23060 current_bottom_left[1] = centroid_vertices[e][1];
23061 // Re-assign the index
23062 index = e;
23063 } // if (centroid_vertices[e][1] < current_bottom_left[1])
23064 else if (centroid_vertices[e][1] == current_bottom_left[1])
23065 {
23066 if (centroid_vertices[e][0] < current_bottom_left[0])
23067 {
23068 // Re-set the current bottom left vertex
23069 current_bottom_left[0] = centroid_vertices[e][0];
23070 current_bottom_left[1] = centroid_vertices[e][1];
23071 // Re-assign the index
23072 index = e;
23073 } // if (centroid_vertices[e][0] < current_bottom_left[0])
23074
23075 } // else if (centroid_vertices[e][1] == current_bottom_left[1])
23076
23077 } // if (!done_face[e])
23078
23079 } // for (e < n_face_ele)
23080
23081 // The face element
23082 unsorted_face_ele_pt[iproc].push_back(
23083 tmp_unsorted_face_ele_pt[iproc][index]);
23084 // The boundary element
23085 unsorted_ele_pt[iproc].push_back(tmp_unsorted_ele_pt[iproc][index]);
23086 // The face index
23087 unsorted_face_index_ele[iproc].push_back(
23088 tmp_unsorted_face_index_ele[iproc][index]);
23089 // The edge boundary associated to the face element
23090 edge_boundary[iproc].push_back(tmp_edge_boundary[iproc][index]);
23091 // The treat as inverted condition
23092 treat_as_inverted[iproc].push_back(
23093 tmp_treat_as_inverted[iproc][index]);
23094
23095 // Mark the face element as sorted (done or visited)
23096 done_face[index] = true;
23097
23098 // Increase the number of sorted bottom-left face elements
23099 n_sorted_bottom_left++;
23100
23101 } // while (n_sorted_bottom_left < n_face_ele)
23102
23103#ifdef PARANOID
23104 // Get the number of face elements sorted with the bottom-left
23105 // condition
23106 const unsigned tmp_n_face_ele = unsorted_face_ele_pt[iproc].size();
23107
23108 if (tmp_n_face_ele != n_face_ele)
23109 {
23110 std::ostringstream error_stream;
23111 error_stream
23112 << "The number of face elements before sorting them starting\n"
23113 << "from their bottom-left vertex is different from the number\n"
23114 << "of face elements after the sorting\n"
23115 << "N. ele before sorting: (" << n_face_ele << ")\n"
23116 << "N. ele after sorting: (" << tmp_n_face_ele << ")\n";
23117 throw OomphLibError(
23118 error_stream.str(),
23119 "RefineableTriangleMesh::create_new_shared_boundaries()",
23120 OOMPH_EXCEPTION_LOCATION);
23121 }
23122#endif
23123
23124 } // if (iproc != my_rank)
23125
23126 } // for (iproc < nproc)
23127
23128 // The time to sort shared face elements
23129 if (Print_timings_level_load_balance > 2)
23130 {
23131 oomph_info << "CPU for sorting shared boundary face elements (load "
23132 "balance) [9.2]: "
23133 << TimingHelpers::timer() - tt_start_sort_shared_face_elements
23134 << std::endl;
23135 }
23136
23137 // ================================================================
23138 // END: SORTING THE SHARED FACE ELEMENTS AND ITS ASSOCIATED DATA, WE
23139 // NEED TO ENSURE THAT THEY APPEAR (OR ARE STORED) IN THE SAME ORDER
23140 // IN BOTH OF THE PROCESSORS THAT CREATED THEM. WE USE THE
23141 // BOTTOM-LEFT NODE OF EACH FACE ELEMENT TO STORE THEM IN THE SAME
23142 // ORDER IN BOTH PROCESSORS. ALSO ENSURE THAT THE FACE ELEMENTS
23143 // AGREE WITH THE FIRST AND LAST NODE IN ALL PROCESSORS
23144 // ================================================================
23145
23146 // ================================================================
23147 // BEGIN: COMPUTE THE GLOBAL DEGREE (VALENCY OF EACH NODE). THE
23148 // DEGREE OF THE NODES IN THE CURRENT SHARED BOUNDARIES IS COMPUTED
23149 // FIRST, THEN THIS INFO. IS SENT TO A ROOT PROCESSOR WHICH IS IN
23150 // CHARGE OF IDENTIFY AND RE-ASSIGN THE DEGREE OF THE NODES (IF THAT
23151 // IS THE CASE)
23152 // ================================================================
23153
23154 // Get the time to compute the valency of each node
23155 double tt_start_compute_valency_of_nodes = 0.0;
23156 if (Print_timings_level_load_balance > 2)
23157 {
23158 tt_start_compute_valency_of_nodes = TimingHelpers::timer();
23159 }
23160
23161 // Stores the global-degree of each node
23162 std::map<Node*, unsigned> global_node_degree;
23163
23164 // Get the global degree (valency) of each node
23165 compute_shared_node_degree_helper(unsorted_face_ele_pt, global_node_degree);
23166
23167 // The time to compute the valency of each node
23168 if (Print_timings_level_load_balance > 2)
23169 {
23171 << "CPU for computing the valency of nodes (load balance) [9.3]: "
23172 << TimingHelpers::timer() - tt_start_compute_valency_of_nodes
23173 << std::endl;
23174 }
23175
23176 // ================================================================
23177 // END: COMPUTE THE GLOBAL DEGREE (VALENCY OF EACH NODE). THE
23178 // DEGREE OF THE NODES IN THE CURRENT SHARED BOUNDARIES IS COMPUTED
23179 // FIRST, THEN THIS INFO. IS SENT TO A ROOT PROCESSOR WHICH IS IN
23180 // CHARGE OF IDENTIFY AND RE-ASSIGN THE DEGREE OF THE NODES (IF THAT
23181 // IS THE CASE)
23182 // ================================================================
23183
23184 // ================================================================
23185 // BEGIN: IDENTIFY THE NODES LYING ON EDGES NOT OVERLAPED BY SHARED
23186 // BOUNDARIES, IDENTIFY THE BOUNDARY TO WHICH THE EDGE CORRESPOND
23187 // ================================================================
23188
23189 // Get the time to compute nodes on non overlapped shared boundaries
23190 double tt_start_nodes_on_non_overlapped_shd_bnd = 0.0;
23191 if (Print_timings_level_load_balance > 2)
23192 {
23193 tt_start_nodes_on_non_overlapped_shd_bnd = TimingHelpers::timer();
23194 }
23195
23196 // Mark the nodes on original boundaries not overlapped by shared
23197 // boundaries
23198 std::map<unsigned, std::map<Node*, bool>>
23199 node_on_bnd_not_overlapped_by_shd_bnd;
23200
23201 // Loop over the edges of the original boundaries
23202 for (std::map<std::pair<Node*, Node*>, unsigned>::iterator it_map =
23203 elements_edges_on_boundary.begin();
23204 it_map != elements_edges_on_boundary.end();
23205 it_map++)
23206 {
23207 // Get the edge
23208 std::pair<Node*, Node*> edge_pair = (*it_map).first;
23209 // Is the edge overlaped by a shared boundary
23210 if (!overlapped_edge[edge_pair])
23211 {
23212 // Mark the nodes of the edge as being on an edge not overlaped
23213 // by a shared boundary on the boundary the edge is
23214 unsigned b = (*it_map).second;
23215
23216 // Get the left node
23217 Node* left_node_pt = edge_pair.first;
23218 node_on_bnd_not_overlapped_by_shd_bnd[b][left_node_pt] = true;
23219
23220 // Get the right node
23221 Node* right_node_pt = edge_pair.second;
23222 node_on_bnd_not_overlapped_by_shd_bnd[b][right_node_pt] = true;
23223
23224 } // if (!overlapped_edge[edge_pair])
23225
23226 } // Loop over edges to mark those nodes on overlaped edge by
23227 // shared boundaries
23228
23229 // The time to compute nodes on non overlapped shared boundaries
23230 if (Print_timings_level_load_balance > 2)
23231 {
23232 oomph_info << "CPU for computing nodes on non overlapped shared "
23233 "boundaries (load balance) [9.4]: "
23235 tt_start_nodes_on_non_overlapped_shd_bnd
23236 << std::endl;
23237 }
23238
23239 // ================================================================
23240 // END: IDENTIFY THE NODES LYING ON EDGES NOT OVERLAPED BY SHARED
23241 // BOUNDARIES, IDENTIFY THE BOUNDARY TO WHICH THE EDGE CORRESPOND
23242 // ================================================================
23243
23244 // ==================================================================
23245 // BEGIN: SORT THE SHARED BOUNDARY FACE ELEMENTS, ADD FACE ELEMENTS
23246 // TO THE LEFT OR RIGHT OF THE ROOT FACE ELEMENT. STOP ADDING WHEN
23247 // THE MOST LEFT OR MOST RIGHT ELEMENT (NODE) IS ALREADY PART OF
23248 // ANOTHER BOUNDARY (THIS MEANS THAT THE SHARED BOUNDARY THAT IS
23249 // BEING CREATED HAS A CONNECTION). ALSO REMEMBER TO CHECK FOR THE
23250 // CASE WHEN THE MOST LEFT OR MOST RIGHT NODE IS A BOUNDARY NODE OF
23251 // A BOUNDARY THAT NO LONGER EXIST IN THE DOMAIN. AT THE END OF THIS
23252 // SECTION WE WILL HAVE THE NUMBER OF SHARED BOUNDARIES OF THIS
23253 // PROCESSOR WITH OTHERS BUT NOT THE GLOBAL SHARED BOUNDARY ID
23254 // ==================================================================
23255
23256 // Get the time to sort shared boundaries face elements to create a
23257 // continuous representation of the boundary
23258 double tt_start_join_shd_bnd_face_ele = 0.0;
23259 if (Print_timings_level_load_balance > 2)
23260 {
23261 tt_start_join_shd_bnd_face_ele = TimingHelpers::timer();
23262 }
23263
23264 // Face elements that create the shared boundaries (sorted)
23265 Vector<Vector<Vector<FiniteElement*>>> sorted_face_ele_pt(nproc);
23266
23267 // Bulk elements that create the shared boundaries (sorted)
23268 Vector<Vector<Vector<FiniteElement*>>> sorted_ele_pt(nproc);
23269
23270 // Face indexes of the bulk elements that create the shared
23271 // boundaries (sorted)
23272 Vector<Vector<Vector<int>>> sorted_face_index_ele(nproc);
23273
23274 // Store the edge boundary id associated with a shared boundary (if
23275 // any, this apply for shared boundaries lying on internal
23276 // boundaries, then the shared boundary is marked as overlaping an
23277 // internal boundary)
23278 Vector<Vector<int>> edge_boundary_id(nproc);
23279
23280 // Store the connection information obtained when joining the face
23281 // elements (used for connection purposes only)
23282 Vector<Vector<Vector<int>>> sorted_connection_info(nproc);
23283
23284 // Store the local shared boundary id associated to the elements
23285 // that will give rise to the shared boundaries (used to compute the
23286 // global shared boundary id from the local shared boundary id)
23287 Vector<Vector<unsigned>> proc_local_shared_boundary_id(nproc);
23288
23289 // Map that associates the local shared boundary id with the list of
23290 // nodes that create it
23291 std::map<unsigned, std::list<Node*>>
23292 local_shd_bnd_id_to_sorted_list_node_pt;
23293
23294 // Local shared bouonday id (used to locally identify the lists of
23295 // nodes that create shared boundaries, it is also useful to
23296 // identify connections with shared boundaries)
23297 unsigned local_shd_bnd_id = this->Initial_shared_boundary_id;
23298
23299 // Sort the face elements, using the nodes at its ends
23300
23301 // Mark the done elements
23302 std::map<FiniteElement*, bool> done_ele;
23303
23304 // Mark the inverted elements
23305 std::map<FiniteElement*, bool> is_inverted;
23306
23307 // Sort the face elements to get the number of shared boundaries
23308 // with in each processor
23309 for (unsigned iproc = 0; iproc < nproc; iproc++)
23310 {
23311 // No face elements with myself
23312 if (iproc != my_rank)
23313 {
23314 // Get the number of unsorted face elements with the iproc
23315 // processor
23316 const unsigned nunsorted_face_ele = unsorted_face_ele_pt[iproc].size();
23317 // Count the number of sorted face elements
23318 unsigned nsorted_face_ele = 0;
23319
23320 // Iterate until all the face elements have been sorted
23321 while (nsorted_face_ele < nunsorted_face_ele)
23322 {
23323 // Take the first nonsorted element an use it as root element,
23324 // add elements to the left and right until no more elements
23325 // left or until a stop condition is reached (connection,
23326 // boundary node)
23327
23328#ifdef PARANOID
23329 // Flag to indicate if a root element was found
23330 bool found_root_element = false;
23331#endif
23332
23333 // Index of the found root element
23334 unsigned root_index = 0;
23335
23336 // List that contains the sorted face elements
23337 std::list<FiniteElement*> tmp_sorted_face_ele_pt;
23338
23339 // List that contains the sorted elements
23340 std::list<FiniteElement*> tmp_sorted_ele_pt;
23341
23342 // List that contains the sorted face indexes of the bulk
23343 // elements
23344 std::list<int> tmp_sorted_face_index_ele;
23345
23346 // Storing for the sorting nodes extracted from the face
23347 // elements. The sorted nodes are used to identify connections
23348 // among new shared boundaries or original boundaries
23349 std::list<Node*> tmp_sorted_nodes_pt;
23350 // Clear the storage (just in case)
23351 tmp_sorted_nodes_pt.clear();
23352
23353 // The initial and final nodes
23354 Node* initial_node_pt = 0;
23355 Node* final_node_pt = 0;
23356
23357 // Store the original boundary id related with the root face
23358 // element (if there is one)
23359 int root_edge_bound_id = -1;
23360
23361 // Loop over the unsorted face elements until a root element
23362 // is found
23363 for (unsigned e = 0; e < nunsorted_face_ele; e++)
23364 {
23365 // Get a root element
23366 FiniteElement* root_ele_pt = unsorted_face_ele_pt[iproc][e];
23367 // Is the element already done?
23368 if (!done_ele[root_ele_pt])
23369 {
23370 // Get the edge boundary id associated with the edge (if
23371 // there is one)
23372 root_edge_bound_id = edge_boundary[iproc][e];
23373 // Add the face element to the list of sorted face
23374 // elements
23375 tmp_sorted_face_ele_pt.push_back(root_ele_pt);
23376 // Add the bulk element to the list of sorted elements
23377 tmp_sorted_ele_pt.push_back(unsorted_ele_pt[iproc][e]);
23378 // Add the face index to the list of sorted face index
23379 // elements
23380 tmp_sorted_face_index_ele.push_back(
23381 unsorted_face_index_ele[iproc][e]);
23382
23383 // Get the nodes and state them as initial and final
23384 const unsigned nnodes = root_ele_pt->nnode();
23385 // Check if the face element should be treated as inverted
23386 if (!treat_as_inverted[iproc][e])
23387 {
23388 initial_node_pt = root_ele_pt->node_pt(0);
23389 final_node_pt = root_ele_pt->node_pt(nnodes - 1);
23390 }
23391 else
23392 {
23393 initial_node_pt = root_ele_pt->node_pt(nnodes - 1);
23394 final_node_pt = root_ele_pt->node_pt(0);
23395 }
23396 // Add both nodes to the list of sorted nodes
23397 tmp_sorted_nodes_pt.push_back(initial_node_pt);
23398 tmp_sorted_nodes_pt.push_back(final_node_pt);
23399
23400 // Mark the element as done
23401 done_ele[root_ele_pt] = true;
23402 // Check if the face element should be treated as inverted
23403 if (!treat_as_inverted[iproc][e])
23404 {
23405 // Mark the element as not inverted
23406 is_inverted[root_ele_pt] = false;
23407 }
23408 else
23409 {
23410 // Mark the element as inverted
23411 is_inverted[root_ele_pt] = true;
23412 }
23413 // Increase the counter for sorted face elements
23414 nsorted_face_ele++;
23415 // Set the root index
23416 root_index = e;
23417#ifdef PARANOID
23418 // Set the flag of found root element
23419 found_root_element = true;
23420#endif
23421 // Break the loop
23422 break;
23423
23424 } // if (!done_ele[root_ele_pt])
23425
23426 } // for (e < nunsorted_face_ele)
23427
23428#ifdef PARANOID
23429 if (!found_root_element)
23430 {
23431 std::ostringstream error_stream;
23432 error_stream
23433 << "It was not possible the found the root element\n\n";
23434 throw OomphLibError(error_stream.str(),
23435 OOMPH_CURRENT_FUNCTION,
23436 OOMPH_EXCEPTION_LOCATION);
23437 }
23438#endif
23439
23440 // New element added. Continue adding elements -- or nodes --
23441 // to the list of shared boundary elements while a new element
23442 // has been added to the list (we have just added the root
23443 // element)
23444 bool new_element_added = true;
23445
23446 // Similarly that in the
23447 // "create_polylines_from_halo_elements_helper() method, we
23448 // extract the nodes (in order) that will create the shared
23449 // polyline, and also check for connections with the just
23450 // added face elements (nodes)
23451
23452 // Flags to indicate at which end (of the sorted list of
23453 // boundary elements) the element was added (left or right)
23454 bool element_added_to_the_left = false;
23455 bool element_added_to_the_right = false;
23456
23457 // Flag to indicate that the "left" node of the element added
23458 // to the left was found to be shared with another boundary
23459 bool connection_to_the_left = false;
23460
23461 // Flag to indicate that the "right" node of the element added
23462 // to the right was found to be shared with another boundary
23463 bool connection_to_the_right = false;
23464
23465 // Flag to stop the adding of elements (and nodes) to the
23466 // current shared boundary (because there are connections at
23467 // both ends)
23468 bool current_polyline_has_connections_at_both_ends = false;
23469
23470 // Store the boundary ids of the polylines to connect (only
23471 // used when the polyline was found to have a connection)
23472 // -1: Indicates no connection
23473 // -2: Indicates connection with itself
23474 // -3: Indicates no connection BUT STOP adding elements
23475 // -because the node is a boundary node whose boundary is no
23476 // -currently part of the domain. Think in one of the corner
23477 // -nodes of a triangle touchin a boundary that does no longer
23478 // -exist
23479 // Any other value: Boundary id to connect
23480 int bound_id_connection_to_the_left = -1;
23481 int bound_id_connection_to_the_right = -1;
23482
23483 // Get the global degree of the node (notice the local degree
23484 // has been updated to global degree)
23485 const unsigned initial_node_degree =
23486 global_node_degree[initial_node_pt];
23487
23488 // Flag to indicate we are calling the method from a load
23489 // balance sub-rutine
23490 const bool called_for_load_balance = true;
23491
23492 // Check if the nodes of the root element have connections
23493 // ... to the left
23494 bound_id_connection_to_the_left =
23495 this->check_connections_of_polyline_nodes(
23496 element_in_processor_pt,
23497 root_edge_bound_id,
23498 overlapped_edge,
23499 node_on_bnd_not_overlapped_by_shd_bnd,
23500 tmp_sorted_nodes_pt,
23501 local_shd_bnd_id_to_sorted_list_node_pt,
23502 initial_node_degree,
23503 initial_node_pt,
23504 called_for_load_balance);
23505
23506 // If there is a stop condition then set the corresponding
23507 // flag
23508 if (bound_id_connection_to_the_left != -1)
23509 {
23510 connection_to_the_left = true;
23511 } // if (bound_id_connection_to_the_left != -1)
23512
23513 // Get the global degree of the node (notice the local degree
23514 // has been updated to global degree)
23515 const unsigned final_node_degree = global_node_degree[final_node_pt];
23516
23517 // ... and to the right
23518 bound_id_connection_to_the_right =
23519 this->check_connections_of_polyline_nodes(
23520 element_in_processor_pt,
23521 root_edge_bound_id,
23522 overlapped_edge,
23523 node_on_bnd_not_overlapped_by_shd_bnd,
23524 tmp_sorted_nodes_pt,
23525 local_shd_bnd_id_to_sorted_list_node_pt,
23526 final_node_degree,
23527 final_node_pt,
23528 called_for_load_balance);
23529
23530 // If there is a stop condition then set the corresponding
23531 // flag
23532 if (bound_id_connection_to_the_right != -1)
23533 {
23534 connection_to_the_right = true;
23535 } // if (bound_id_connection_to_the_right != -1)
23536
23537 // If the current shared boundary has connections at both ends
23538 // then stop the adding of elements (and nodes)
23539 if (connection_to_the_left && connection_to_the_right)
23540 {
23541 current_polyline_has_connections_at_both_ends = true;
23542 }
23543
23544 // Continue searching for more elements to add if
23545 // 1) A new element was added at the left or right of the list
23546 // 2) There are more possible elements to add
23547 // 3) The nodes at the edges of the added element (left or
23548 // right) are not part of any other previous shared
23549 // boundary
23550 while (new_element_added && (nsorted_face_ele < nunsorted_face_ele) &&
23551 !current_polyline_has_connections_at_both_ends)
23552 {
23553 // Loop over the remaining elements and try to create a
23554 // contiguous set of face elements, start looking from the
23555 // root index. Any previous element should have been already
23556 // visited
23557 for (unsigned e = root_index; e < nunsorted_face_ele; e++)
23558 {
23559 // Reset the flags for added elements, to the left and right
23560 new_element_added = false;
23561 element_added_to_the_left = false;
23562 element_added_to_the_right = false;
23563
23564 // Get the "e"-th element on the vector
23565 FiniteElement* tmp_ele_pt = unsorted_face_ele_pt[iproc][e];
23566 // Get the boundary id associated with the edge (if any)
23567 const int edge_bound_id = edge_boundary[iproc][e];
23568 // Check if the element has been already sorted and the
23569 // related edge bound id is the same as the root edge (if
23570 // any)
23571 if (!done_ele[tmp_ele_pt] &&
23572 (edge_bound_id == root_edge_bound_id))
23573 {
23574 // Get the number of nodes on the current element
23575 const unsigned nnodes = tmp_ele_pt->nnode();
23576 // Get the first and last node of the element
23577 // Check if the face element should be treated as inverted
23578 Node* first_node_pt = 0;
23579 Node* last_node_pt = 0;
23580 if (!treat_as_inverted[iproc][e])
23581 {
23582 first_node_pt = tmp_ele_pt->node_pt(0);
23583 last_node_pt = tmp_ele_pt->node_pt(nnodes - 1);
23584 }
23585 else
23586 {
23587 first_node_pt = tmp_ele_pt->node_pt(nnodes - 1);
23588 last_node_pt = tmp_ele_pt->node_pt(0);
23589 }
23590
23591 // A pointer to the node at the left or right of the
23592 // just added element, the most left or the most right
23593 // node
23594 Node* new_added_node_pt = 0;
23595
23596 // Check if the element goes to the left
23597 if (initial_node_pt == last_node_pt && !connection_to_the_left)
23598 {
23599 // Update the initial node and the just added node
23600 new_added_node_pt = initial_node_pt = first_node_pt;
23601 // Add the most left node
23602 tmp_sorted_nodes_pt.push_front(first_node_pt);
23603 // Add the face element to the list of sorted face
23604 // elements
23605 tmp_sorted_face_ele_pt.push_front(tmp_ele_pt);
23606 // Add the bulk element to the list of sorted elements
23607 tmp_sorted_ele_pt.push_front(unsorted_ele_pt[iproc][e]);
23608 // Add the face index to the list of sorted face index
23609 // elements
23610 tmp_sorted_face_index_ele.push_front(
23611 unsorted_face_index_ele[iproc][e]);
23612 if (!treat_as_inverted[iproc][e])
23613 {
23614 // Mark the element as not inverted
23615 is_inverted[tmp_ele_pt] = false;
23616 }
23617 else
23618 {
23619 // Mark the element as inverted
23620 is_inverted[tmp_ele_pt] = true;
23621 }
23622 // Set the flag to indicate a new element was added
23623 new_element_added = true;
23624 // Set the flag to indicate the element was added to
23625 // the left
23626 element_added_to_the_left = true;
23627 }
23628 // Check if the element goes to the left (but inverted)
23629 else if (initial_node_pt == first_node_pt &&
23630 !connection_to_the_left)
23631 {
23632 // Update the initial node and the just added node
23633 new_added_node_pt = initial_node_pt = last_node_pt;
23634 // Add the most left node
23635 tmp_sorted_nodes_pt.push_front(last_node_pt);
23636 // Add the face element to the list of sorted face
23637 // elements
23638 tmp_sorted_face_ele_pt.push_front(tmp_ele_pt);
23639 // Add the bulk element to the list of sorted elements
23640 tmp_sorted_ele_pt.push_front(unsorted_ele_pt[iproc][e]);
23641 // Add the face index to the list of sorted face index
23642 // elements
23643 tmp_sorted_face_index_ele.push_front(
23644 unsorted_face_index_ele[iproc][e]);
23645 if (!treat_as_inverted[iproc][e])
23646 {
23647 // Mark the element as inverted
23648 is_inverted[tmp_ele_pt] = true;
23649 }
23650 else
23651 {
23652 // Mark the element as not inverted
23653 is_inverted[tmp_ele_pt] = false;
23654 }
23655 // Set the flag to indicate a new element was added
23656 new_element_added = true;
23657 // Set the flag to indicate the element was added to
23658 // the left
23659 element_added_to_the_left = true;
23660 }
23661 // Check if the elements goes to the right
23662 else if (final_node_pt == first_node_pt &&
23663 !connection_to_the_right)
23664 {
23665 // Update the final node and the just added node
23666 new_added_node_pt = final_node_pt = last_node_pt;
23667 // Add the most right node
23668 tmp_sorted_nodes_pt.push_back(last_node_pt);
23669 // Add the face element to the list of sorted face
23670 // elements
23671 tmp_sorted_face_ele_pt.push_back(tmp_ele_pt);
23672 // Add the bulk element to the list of sorted elements
23673 tmp_sorted_ele_pt.push_back(unsorted_ele_pt[iproc][e]);
23674 // Add the face index to the list of sorted face index
23675 // elements
23676 tmp_sorted_face_index_ele.push_back(
23677 unsorted_face_index_ele[iproc][e]);
23678 if (!treat_as_inverted[iproc][e])
23679 {
23680 // Mark the element as not inverted
23681 is_inverted[tmp_ele_pt] = false;
23682 }
23683 else
23684 {
23685 // Mark the element as inverted
23686 is_inverted[tmp_ele_pt] = true;
23687 }
23688 // Set the flag to indicate a new element was added
23689 new_element_added = true;
23690 // Set the flag to indicate the element was added to
23691 // the right
23692 element_added_to_the_right = true;
23693 }
23694 // Check if the elements goes to the right (but inverted)
23695 else if (final_node_pt == last_node_pt &&
23696 !connection_to_the_right)
23697 {
23698 // Update the final node and the just added node
23699 new_added_node_pt = final_node_pt = first_node_pt;
23700 // Add the most right node
23701 tmp_sorted_nodes_pt.push_back(first_node_pt);
23702 // Add the face element to the list of sorted face
23703 // elements
23704 tmp_sorted_face_ele_pt.push_back(tmp_ele_pt);
23705 // Add the bulk element to the list of sorted elements
23706 tmp_sorted_ele_pt.push_back(unsorted_ele_pt[iproc][e]);
23707 // Add the face index to the list of sorted face index
23708 // elements
23709 tmp_sorted_face_index_ele.push_back(
23710 unsorted_face_index_ele[iproc][e]);
23711 if (!treat_as_inverted[iproc][e])
23712 {
23713 // Mark the element as inverted
23714 is_inverted[tmp_ele_pt] = true;
23715 }
23716 else
23717 {
23718 // Mark the element as not inverted
23719 is_inverted[tmp_ele_pt] = false;
23720 }
23721 // Set the flag to indicate a new elements was added
23722 new_element_added = true;
23723 // Set the flag to indicate the element was added to
23724 // the right
23725 element_added_to_the_right = true;
23726 }
23727
23728 // Do additional stuff if the element was added
23729 if (new_element_added)
23730 {
23731 // Mark the element as done
23732 done_ele[tmp_ele_pt] = true;
23733 // Increase the counter for sorted face elements
23734 nsorted_face_ele++;
23735
23736 // Get the global degree of the node (notice the
23737 // local degree has been updated to global degree)
23738 const unsigned new_added_node_degree =
23739 global_node_degree[new_added_node_pt];
23740
23741 // Based on which side the element was added, look for
23742 // connections on that side
23743
23744 // Verify for connections to the left (we need to
23745 // check for the connection variable too, since
23746 // after a connection has been done we no longer
23747 // need to verify for this condition)
23748 if (element_added_to_the_left && !connection_to_the_left)
23749 {
23750 // Check for connection
23751 bound_id_connection_to_the_left =
23752 this->check_connections_of_polyline_nodes(
23753 element_in_processor_pt,
23754 root_edge_bound_id,
23755 overlapped_edge,
23756 node_on_bnd_not_overlapped_by_shd_bnd,
23757 tmp_sorted_nodes_pt,
23758 local_shd_bnd_id_to_sorted_list_node_pt,
23759 new_added_node_degree,
23760 new_added_node_pt,
23761 called_for_load_balance);
23762
23763 // If there is a stop condition then set the
23764 // corresponding flag
23765 if (bound_id_connection_to_the_left != -1)
23766 {
23767 connection_to_the_left = true;
23768 } // if (bound_id_connection_to_the_left != -1)
23769
23770 } // if (node_added_to_the_left &&
23771 // !connection_to_the_left)
23772
23773 // Verify for connections to the right (we need to
23774 // check for the connection variable too, since
23775 // after a connection has been done we no longer
23776 // need to verify for this condition)
23777 if (element_added_to_the_right && !connection_to_the_right)
23778 {
23779 // Check for connection
23780 bound_id_connection_to_the_right =
23781 this->check_connections_of_polyline_nodes(
23782 element_in_processor_pt,
23783 root_edge_bound_id,
23784 overlapped_edge,
23785 node_on_bnd_not_overlapped_by_shd_bnd,
23786 tmp_sorted_nodes_pt,
23787 local_shd_bnd_id_to_sorted_list_node_pt,
23788 new_added_node_degree,
23789 new_added_node_pt,
23790 called_for_load_balance);
23791
23792 // If there is a stop condition then set the
23793 // corresponding flag
23794 if (bound_id_connection_to_the_right != -1)
23795 {
23796 connection_to_the_right = true;
23797 } // if (bound_id_connection_to_the_right != -1)
23798
23799 } // if (node_added_to_the_right &&
23800 // !connection_to_the_right)
23801
23802 // If the current shared boundary has connections at
23803 // both ends then stop the adding of elements (and
23804 // nodes)
23805 if (connection_to_the_left && connection_to_the_right)
23806 {
23807 current_polyline_has_connections_at_both_ends = true;
23808 }
23809
23810 // Break the for (looping over unsorted face
23811 // elements) and re-start looking for more elements
23812 // that fit to the left or right
23813 break;
23814
23815 } // if (new_element_added)
23816
23817 } // if (!done_ele[tmp_ele_pt])
23818
23819 } // for (e < nunsorted_face_ele)
23820
23821 } // while(new_element_added &&
23822 // (nsorted_face_ele < nunsorted_face_ele)
23823 // && !current_polyline_has_connections_at_both_ends)
23824
23825 // ------------------------------------------------------------
23826 // Before assigning a local shared boundary id to the list of
23827 // nodes and boundary elements, check for any loop that the
23828 // shared boundary may be creating
23829
23830 // The vector of the elements
23831 Vector<FiniteElement*> tmp_vector_sorted_ele_pt;
23832 // Store the list of elements on a vector of elements
23833 for (std::list<FiniteElement*>::iterator it =
23834 tmp_sorted_ele_pt.begin();
23835 it != tmp_sorted_ele_pt.end();
23836 it++)
23837 {
23838 tmp_vector_sorted_ele_pt.push_back((*it));
23839 }
23840
23841 // The vector of the face elements
23842 Vector<FiniteElement*> tmp_vector_sorted_face_ele_pt;
23843 // Store the list of face elements on a vector of face
23844 // elements
23845 for (std::list<FiniteElement*>::iterator it =
23846 tmp_sorted_face_ele_pt.begin();
23847 it != tmp_sorted_face_ele_pt.end();
23848 it++)
23849 {
23850 tmp_vector_sorted_face_ele_pt.push_back((*it));
23851 }
23852
23853 // The vector of the face indexes
23854 Vector<int> tmp_vector_sorted_face_index_ele;
23855 // Store the list of elements on a vector of elements
23856 for (std::list<int>::iterator it = tmp_sorted_face_index_ele.begin();
23857 it != tmp_sorted_face_index_ele.end();
23858 it++)
23859 {
23860 tmp_vector_sorted_face_index_ele.push_back((*it));
23861 }
23862
23863 // Store the nodes for the new shared polylines without loops
23864 Vector<std::list<Node*>> final_sorted_nodes_pt;
23865 // Store the boundary elements of the shared polyline without
23866 // loops
23867 Vector<Vector<FiniteElement*>> final_boundary_element_pt;
23868 // Store the boundary face elements of the shared polyline
23869 // without loops
23870 Vector<Vector<FiniteElement*>> final_boundary_face_element_pt;
23871 // Face indexes of the boundary elements without loops
23872 Vector<Vector<int>> final_face_index_element;
23873 // Connection flags (to the left) of the shared boundaries
23874 // without loops
23875 Vector<int> final_bound_id_connection_to_the_left;
23876 // Connection flags (to the right) of the shared boundaries
23877 // without loops
23878 Vector<int> final_bound_id_connection_to_the_right;
23879
23880 // Break any possible loop created by the shared polyline
23881 this->break_loops_on_shared_polyline_load_balance_helper(
23882 local_shd_bnd_id,
23883 tmp_sorted_nodes_pt,
23884 tmp_vector_sorted_ele_pt,
23885 tmp_vector_sorted_face_ele_pt,
23886 tmp_vector_sorted_face_index_ele,
23887 bound_id_connection_to_the_left,
23888 bound_id_connection_to_the_right,
23889 final_sorted_nodes_pt,
23890 final_boundary_element_pt,
23891 final_boundary_face_element_pt,
23892 final_face_index_element,
23893 final_bound_id_connection_to_the_left,
23894 final_bound_id_connection_to_the_right);
23895
23896 // Get the number of final sorted nodes
23897 const unsigned n_final_sorted_nodes = final_sorted_nodes_pt.size();
23898
23899 // Loop over the list of final sorted nodes
23900 for (unsigned i = 0; i < n_final_sorted_nodes; i++)
23901 {
23902 // Store the list of nodes that gave rise to the shared
23903 // boundary
23904 local_shd_bnd_id_to_sorted_list_node_pt[local_shd_bnd_id] =
23905 final_sorted_nodes_pt[i];
23906
23907 // Store the local shared boundary id assigned to the
23908 // elements that will create the shared boundary
23909 proc_local_shared_boundary_id[iproc].push_back(local_shd_bnd_id);
23910
23911 // Increase the shared boundary id (note that this is only
23912 // used to keep track of the list of nodes that create the
23913 // shared boundaries in the current processor)
23914 local_shd_bnd_id++;
23915
23916 // Include the vector of elements to the sorted vector
23917 sorted_ele_pt[iproc].push_back(final_boundary_element_pt[i]);
23918
23919 // Include the vector of face elements to the sorted vector
23920 sorted_face_ele_pt[iproc].push_back(
23921 final_boundary_face_element_pt[i]);
23922
23923 // Include the vector of elements to the sorted vector
23924 sorted_face_index_ele[iproc].push_back(final_face_index_element[i]);
23925
23926 // Include the possible associated boundary id to the vector
23927 edge_boundary_id[iproc].push_back(root_edge_bound_id);
23928
23929 // Include the connection information associated with the
23930 // current set of face elements (that will give rise to a
23931 // shared polyline
23932 // The temporal storage for the boundary connections ids
23933 Vector<int> bnd_connections_ids(2);
23934 bnd_connections_ids[0] = final_bound_id_connection_to_the_left[i];
23935 bnd_connections_ids[1] = final_bound_id_connection_to_the_right[i];
23936 sorted_connection_info[iproc].push_back(bnd_connections_ids);
23937
23938 } // for (i < n_final_sorted_nodes)
23939
23940 } // while (nsorted_face_ele < nunsorted_face_ele)
23941
23942 } // if (iproc != my_rank)
23943
23944 } // for (iproc < nproc)
23945
23946 // The time to sort shared boundaries face elements to create a
23947 // continuous representation of the boundary
23948 if (Print_timings_level_load_balance > 2)
23949 {
23950 oomph_info << "CPU for joining shared boundary face elements (load "
23951 "balance) [9.5]: "
23952 << TimingHelpers::timer() - tt_start_join_shd_bnd_face_ele
23953 << std::endl;
23954 }
23955
23956 // ==================================================================
23957 // END: SORT THE SHARED BOUNDARY FACE ELEMENTS, ADD FACE ELEMENTS TO
23958 // THE LEFT OR RIGHT OF THE ROOT FACE ELEMENT. STOP ADDING WHEN THE
23959 // MOST LEFT OR MOST RIGHT ELEMENT (NODE) IS ALREADY PART OF ANOTHER
23960 // BOUNDARY (THIS MEANS THAT THE SHARED BOUNDARY THAT IS BEING
23961 // CREATED HAS A CONNECTION). ALSO REMEMBER TO CHECK FOR THE CASE
23962 // WHEN THE MOST LEFT OR MOST RIGHT NODE IS A BOUNDARY NODE OF A
23963 // BOUNDARY THAT NO LONGER EXIST IN THE DOMAIN. AT THE END OF THIS
23964 // SECTION WE WILL HAVE THE NUMBER OF SHARED BOUNDARIES OF THIS
23965 // PROCESSOR WITH OTHERS BUT NOT THE GLOBAL SHARED BOUNDARY ID
23966 // ==================================================================
23967
23968 // ==================================================================
23969 // BEGIN: COMPUTE THE GLOBAL SHARED BOUNDARIES IDS. GATHER THE
23970 // NUMBER OF SHARED BOUNDARIES OF EACH PROCESSOR, THEN A ROOT
23971 // PROCESSOR IS IN CHARGE OF VERIFYING THAT THE SAME NUMBER OF
23972 // SHARED BOUNDARIES HAVE BEEN CREATED BY A PAIR OF PROCESSORS. THE
23973 // ROOT PROCESSOR COMPUTES THE INITIAL GLOBAL SHARED BOUNDARY ID
23974 // BETWEEN EACH PAIR OR PROCESSORS AND SENDS THESE INFO. TO ALL
23975 // PROCESSORS. THE GLOBAL INITIAL AND FINAL SHARED BOUNDARY ID ARE
23976 // ALSO COMPUTED
23977 // ==================================================================
23978
23979 // Get the time to compute new shared boundaries ids
23980 double tt_start_get_new_shared_boundaries_ids = 0.0;
23981 if (Print_timings_level_load_balance > 2)
23982 {
23983 tt_start_get_new_shared_boundaries_ids = TimingHelpers::timer();
23984 }
23985
23986 // Get the number of shared boundaries with in each processor
23987 Vector<unsigned> nshared_boundaries_with_processor(nproc);
23988 // Loop over the processors
23989 for (unsigned iproc = 0; iproc < nproc; iproc++)
23990 {
23991 // No shared boundaries with myself
23992 if (iproc != my_rank)
23993 {
23994 // Store the number of shared boundaries of the current
23995 // processor (my_rank) with the iproc processor
23996 nshared_boundaries_with_processor[iproc] =
23997 sorted_face_ele_pt[iproc].size();
23998
23999 } // if (iproc != my_rank)
24000
24001 } // for (iproc < nproc)
24002
24003 // Each processor sends the number of shared boundaries that it has
24004 // with in each other processor to the "root_processor" which will
24005 // be in charge of checking and computing the global shared
24006 // boundaries ids
24007 const unsigned root_processor = 0;
24008
24009 // Get the communicator of the mesh
24010 OomphCommunicator* comm_pt = this->communicator_pt();
24011
24012 // Container where to store the info. received from other processor
24013 // in root. It receives from all processors the number of shared
24014 // boundaries that each one has with any other processor
24015 Vector<unsigned> flat_unsigned_root_received_data(nproc * nproc);
24016
24017 // Gather the info. in the "root_processor"
24018 MPI_Gather(&nshared_boundaries_with_processor[0], // Info. sent from
24019 // each processor
24020 nproc, // Total number of data to send from each
24021 // processor
24022 MPI_UNSIGNED,
24023 &flat_unsigned_root_received_data[0], // Container where
24024 // to receive the
24025 // info. from all
24026 // the processors
24027 nproc, // Number of data to receive from each processor
24028 MPI_UNSIGNED,
24029 root_processor, // The processor that receives all the
24030 // info.
24031 comm_pt->mpi_comm());
24032
24033 // Container where root store the info. that will be sent back to
24034 // all processor, because root performs a Broadcast operation then
24035 // the info. is received in the same container
24036 Vector<unsigned> flat_unsigned_root_send_receive_data;
24037
24038 // Compute the new initial and final shared boundary id (they are
24039 // based on the global number of shared boundaries)
24040 unsigned new_initial_shared_boundary_id = 0;
24041 unsigned new_final_shared_boundary_id = 0;
24042
24043 // Compute the boundaries ids for the shared boundaries
24044 if (my_rank == root_processor)
24045 {
24046 // Change the representation of the data received from all
24047 // processors to a matrix representation for ease access
24048 Vector<Vector<unsigned>> root_nshared_bound_proc_with_proc(nproc);
24049 // Loop over the processors and get the number of shared
24050 // boundaries of processor iproc with jproc
24051 for (unsigned iproc = 0; iproc < nproc; iproc++)
24052 {
24053 // Resize the vector to store the data
24054 root_nshared_bound_proc_with_proc[iproc].resize(nproc);
24055 // Loop over the processors and get the number of shared
24056 // boundaries of processor iproc with jproc
24057 for (unsigned jproc = 0; jproc < nproc; jproc++)
24058 {
24059 root_nshared_bound_proc_with_proc[iproc][jproc] =
24060 flat_unsigned_root_received_data[(iproc * nproc) + jproc];
24061
24062 } // for (jproc < nproc)
24063
24064 } // for (iproc < nproc)
24065
24066#ifdef PARANOID
24067 // Check that the same number of boundaries are shared by two
24068 // specific processors
24069 for (unsigned iproc = 0; iproc < nproc; iproc++)
24070 {
24071 for (unsigned jproc = 0; jproc < iproc; jproc++)
24072 {
24073 if (root_nshared_bound_proc_with_proc[iproc][jproc] !=
24074 root_nshared_bound_proc_with_proc[jproc][iproc])
24075 {
24076 std::ostringstream error_stream;
24077 error_stream
24078 << "ROOT PROCESSOR ERROR\n\n"
24079 << "The number of shared boundaries between processor (" << iproc
24080 << ") and (" << jproc << ") is not the same:\n"
24081 << "Shared boundaries of processor (" << iproc
24082 << ") with processor (" << jproc << "): ("
24083 << root_nshared_bound_proc_with_proc[iproc][jproc] << ")\n"
24084 << "Shared boundaries of processor (" << jproc
24085 << ") with processor (" << iproc << "): ("
24086 << root_nshared_bound_proc_with_proc[jproc][iproc] << ")\n\n";
24087 throw OomphLibError(error_stream.str(),
24088 OOMPH_CURRENT_FUNCTION,
24089 OOMPH_EXCEPTION_LOCATION);
24090
24091 } // The number of shared boundaries between processors
24092 // "iproc" and "jproc" is not the same
24093
24094 } // for (jproc < iproc)
24095
24096 } // for (iproc < nproc)
24097#endif
24098
24099 // The enumeration of the shared boundaries starts from the lowest
24100 // processor number to the highest processor number
24101
24102 // Two processors share the same boundaries ids, the lowest
24103 // processor number is the one in charge of computing the shared
24104 // boundaries ids
24105 Vector<Vector<unsigned>> start_shared_bound_id_proc_with_proc(nproc);
24106 // Resize the vector, we can not do it when storing the
24107 // info. because of the strategy to save the info.
24108 for (unsigned iproc = 0; iproc < nproc; iproc++)
24109 {
24110 start_shared_bound_id_proc_with_proc[iproc].resize(nproc);
24111 }
24112
24113 // The shared boundaries ids start from the current number of
24114 // original boundaries
24115 unsigned shared_bound_id = this->nboundary();
24116
24117 // Set the new initial shared boundary id
24118 new_initial_shared_boundary_id = shared_bound_id;
24119
24120 // Assign the global shared boundary id for the shared boundaries
24121 for (unsigned iproc = 0; iproc < nproc; iproc++)
24122 {
24123 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
24124 {
24125 // Are there shared boundaries between the pair of processors
24126 if (root_nshared_bound_proc_with_proc[iproc][jproc] > 0)
24127 {
24128 // Set the start boundary id of processor "iproc" with
24129 // processor "jproc" and viceversa
24130 start_shared_bound_id_proc_with_proc[iproc][jproc] =
24131 shared_bound_id;
24132 start_shared_bound_id_proc_with_proc[jproc][iproc] =
24133 shared_bound_id;
24134 // Increase the shared boundary id counter with as many
24135 // shared boundaries there are between the iproc and jproc
24136 // processor
24137 shared_bound_id += root_nshared_bound_proc_with_proc[iproc][jproc];
24138 } // if (root_nshared_bound_proc_with_proc[iproc][jproc] > 0)
24139
24140 } // for (jproc < iproc)
24141
24142 } // for (iproc < nproc)
24143
24144 // Set the new final shared boundary id
24145 new_final_shared_boundary_id = shared_bound_id;
24146
24147 // Prepare the info. to send back to each processor
24148 Vector<unsigned> send_start_shared_bound_id_proc_with_proc(nproc * nproc);
24149
24150 // Copy the info. to the storage to send the info. back to other
24151 // processors
24152 for (unsigned iproc = 0; iproc < nproc; iproc++)
24153 {
24154 for (unsigned jproc = 0; jproc < nproc; jproc++)
24155 {
24156 // Get the initial shared boundary id between each pair of
24157 // processors (iproc, jproc)
24158 const unsigned initial_shd_bnd_id =
24159 start_shared_bound_id_proc_with_proc[iproc][jproc];
24160 flat_unsigned_root_send_receive_data.push_back(initial_shd_bnd_id);
24161
24162 // .. then copy the number of shared boundaries that there are
24163 // between processor iproc and jproc
24164 const unsigned nshared_bnd_iproc_jproc =
24165 root_nshared_bound_proc_with_proc[iproc][jproc];
24166 flat_unsigned_root_send_receive_data.push_back(
24167 nshared_bnd_iproc_jproc);
24168
24169 } // for (jproc < nproc)
24170
24171 } // for (iproc < nproc)
24172
24173 // .. at the end of the data to send include the global initial
24174 // shared boundary id
24175 flat_unsigned_root_send_receive_data.push_back(
24176 new_initial_shared_boundary_id);
24177
24178 // ... and the global final shared boundary id
24179 flat_unsigned_root_send_receive_data.push_back(
24180 new_final_shared_boundary_id);
24181
24182 } // if (my_rank == root_processor)
24183
24184 // Send the initial shared boundaries ids and the number of shared
24185 // boundaries between all procesors to all processors. All
24186 // processors need to know this info.
24187
24188 // The number of data that will be sent by root to other processors
24189 // and the number of data that other processors receive from root,
24190 // it is the same because it is performed via a Broadcast
24191 unsigned root_ndata_sent_to_all_proc =
24192 flat_unsigned_root_send_receive_data.size();
24193
24194 MPI_Bcast(&root_ndata_sent_to_all_proc, // Data to send
24195 1,
24196 MPI_UNSIGNED,
24197 root_processor,
24198 comm_pt->mpi_comm());
24199
24200 // Resize the container if this is a processor that receives data
24201 if (my_rank != root_processor)
24202 {
24203 flat_unsigned_root_send_receive_data.resize(root_ndata_sent_to_all_proc);
24204 }
24205
24206 // Send back the start boundaries ids for the shared boundaries
24207 // Scatter the info. from the "root_processor"
24208 MPI_Bcast(&flat_unsigned_root_send_receive_data[0], // Info. sent to
24209 // each
24210 // processor
24211 root_ndata_sent_to_all_proc, // Total number of data to
24212 // send to each processor
24213 MPI_UNSIGNED,
24214 root_processor, // The processor that sends all the info.
24215 comm_pt->mpi_comm());
24216
24217 // The container to store the initial shared boundaries ids between
24218 // each pair of processors
24219 Vector<Vector<unsigned>> initial_shared_bound_id_proc_with_proc(nproc);
24220
24221 // All processors need to know how many shared boundaries there are
24222 // between each pair of processors
24223
24224 // The number of shared boundaries between each pair of processors
24225 Vector<Vector<unsigned>> nshared_bound_proc_with_proc(nproc);
24226
24227 unsigned iflat_counter = 0;
24228 // Fill the containers with the received info. from root processor
24229 for (unsigned iproc = 0; iproc < nproc; iproc++)
24230 {
24231 // Resize the containers
24232 initial_shared_bound_id_proc_with_proc[iproc].resize(nproc);
24233 nshared_bound_proc_with_proc[iproc].resize(nproc);
24234
24235 // Loop over the processors
24236 for (unsigned jproc = 0; jproc < nproc; jproc++)
24237 {
24238 // Get the initial shared boundary id between each pair of
24239 // processors (iproc, jproc)
24240 initial_shared_bound_id_proc_with_proc[iproc][jproc] =
24241 flat_unsigned_root_send_receive_data[iflat_counter++];
24242
24243 // .. and copy the number of shared boundaries that there are
24244 // between processor iproc and jproc
24245 nshared_bound_proc_with_proc[iproc][jproc] =
24246 flat_unsigned_root_send_receive_data[iflat_counter++];
24247
24248 } // for (jproc < nproc)
24249
24250 } // for (iproc < nproc)
24251
24252 // Read the new initial shared boundary id
24253 new_initial_shared_boundary_id =
24254 flat_unsigned_root_send_receive_data[root_ndata_sent_to_all_proc - 2];
24255
24256 // Read the new final shared boundary id
24257 new_final_shared_boundary_id =
24258 flat_unsigned_root_send_receive_data[root_ndata_sent_to_all_proc - 1];
24259
24260 // The time to compute new shared boundaries ids
24261 if (Print_timings_level_load_balance > 2)
24262 {
24264 << "CPU for computing new shared boundaries ids (load balance) [9.6]: "
24265 << TimingHelpers::timer() - tt_start_get_new_shared_boundaries_ids
24266 << std::endl;
24267 }
24268
24269 // ==================================================================
24270 // END: COMPUTE THE GLOBAL SHARED BOUNDARIES IDS. GATHER THE NUMBER
24271 // OF SHARED BOUNDARIES OF EACH PROCESSOR, THEN A ROOT PROCESSOR IS
24272 // IN CHARGE OF VERIFYING THAT THE SAME NUMBER OF SHARED BOUNDARIES
24273 // HAVE BEEN CREATED BY A PAIR OF PROCESSORS. THE ROOT PROCESSOR
24274 // COMPUTES THE INITIAL GLOBAL SHARED BOUNDARY ID BETWEEN EACH PAIR
24275 // OR PROCESSORS AND SENDS THESE INFO. TO ALL PROCESSORS. THE GLOBAL
24276 // INITIAL AND FINAL SHARED BOUNDARY ID ARE ALSO COMPUTED
24277 // ==================================================================
24278
24279 // ==================================================================
24280 // BEGIN: CREATE THE NEW SHARED BOUNDARIES. DELETE THE OLD SHARED
24281 // BOUNDARIES INFORMATION. FILL THE DATA STRUCTURES WITH THE NEW
24282 // SHARED BOUNDARIES INFO.
24283 // ==================================================================
24284
24285 // Get the time to create new shared boundaries representations
24286 double tt_start_create_new_shared_boundaries_polylines = 0.0;
24287 if (Print_timings_level_load_balance > 2)
24288 {
24289 tt_start_create_new_shared_boundaries_polylines = TimingHelpers::timer();
24290 }
24291
24292 // Create the shared boundaries and establish all the related info.
24293 // - Create Polylines
24294 // - Store shared boundary elements
24295 // - Fill data structures to know which shared boundaries belong to
24296 // which processor
24297
24298 // Resize the shared polylines container
24299 this->flush_shared_boundary_polyline_pt();
24300 this->Shared_boundary_polyline_pt.resize(nproc);
24301
24302 // Resize for the boundaries ids shared with all processors
24303 this->Shared_boundaries_ids.clear();
24304 this->Shared_boundaries_ids.resize(nproc);
24305 for (unsigned iproc = 0; iproc < nproc; iproc++)
24306 {
24307 this->Shared_boundaries_ids[iproc].clear();
24308 this->Shared_boundaries_ids[iproc].resize(nproc);
24309 } // for (iproc < nproc)
24310
24311 // Clear data
24312 this->Shared_boundary_from_processors.clear();
24313 this->Shared_boundary_overlaps_internal_boundary.clear();
24314 this->Boundary_was_splitted.clear();
24315 this->Boundary_subpolylines.clear();
24316 this->Boundary_marked_as_shared_boundary.clear();
24317
24318 // Flush data
24319 this->flush_shared_boundary_element();
24320 this->flush_face_index_at_shared_boundary();
24321 this->flush_shared_boundary_node();
24322 this->flush_sorted_shared_boundary_node();
24323
24324 // Store the old local inital shared boundary id (used to map from
24325 // local shared boundary id to global shared boundary id)
24326 const unsigned old_local_shd_bnd_id = this->Initial_shared_boundary_id;
24327
24328 // Update the initial and final shared boundary id
24329 this->Initial_shared_boundary_id = new_initial_shared_boundary_id;
24330 this->Final_shared_boundary_id = new_final_shared_boundary_id;
24331
24332 // Storage for the new created polylines between the current
24333 // processor (my_rank) and the other processors, unsorted polylines
24334 Vector<TriangleMeshPolyLine*> unsorted_polylines_pt;
24335
24336 // Map to get the global shared boundary id from the local shared
24337 // boundary id. Note that this is only used to get the global shared
24338 // boundary id when the shared boundary that is being created has
24339 // connections
24340 std::map<unsigned, unsigned> local_to_global_shd_bnd_id;
24341
24342 // Each processor knows the boundaries ids for each of the shared
24343 // boundaries it has, establish that info. in the proper containers
24344 // Additionally, store the shared boundaries of ALL processors with
24345 // ALL processors, but only create the shared boundaries (and their
24346 // respective polylines) of the current processor (my_rank)
24347 for (unsigned iproc = 0; iproc < nproc; iproc++)
24348 {
24349 // Avoid creating double shared boundaries, the shared boundaries
24350 // created between processor "iproc" and processor "jproc" are the
24351 // same than those created between processor "jproc" and processor
24352 // "iproc"
24353 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
24354 {
24355 // If we are working with the current processor (my_rank) then
24356 // create the shared boundaries, if that is not the case then
24357 // only fill the info. on the proper containers
24358 if (iproc == my_rank || jproc == my_rank)
24359 {
24360 // Check the condition that made it get here
24361 unsigned ref_proc = 0;
24362 if (iproc == my_rank)
24363 {
24364 ref_proc = jproc;
24365 }
24366 else if (jproc == my_rank)
24367 {
24368 ref_proc = iproc;
24369 }
24370
24371 // Get the number of shared boundaries between processor iproc
24372 // and processor jproc
24373 const unsigned nshared_bound_iproc_jproc =
24374 nshared_bound_proc_with_proc[iproc][jproc];
24375
24376 // Loop over the number of shared boundaries
24377 for (unsigned counter = 0; counter < nshared_bound_iproc_jproc;
24378 counter++)
24379 {
24380 // Compute the shared boundary id for the shared boundary
24381 const unsigned shd_bnd_id =
24382 initial_shared_bound_id_proc_with_proc[iproc][jproc] + counter;
24383 // Set up the shared boundaries between "iproc" (my_rank)
24384 // and "jproc"
24385 this->Shared_boundaries_ids[iproc][jproc].push_back(shd_bnd_id);
24386 this->Shared_boundaries_ids[jproc][iproc].push_back(shd_bnd_id);
24387
24388 // Specify the processors involved for the creation of the
24389 // shared boundary
24390 Vector<unsigned> processors(2);
24391 processors[0] = iproc;
24392 processors[1] = jproc;
24393 this->Shared_boundary_from_processors[shd_bnd_id] = processors;
24394
24395 // Get the possible root edge id associated to the shared
24396 // boundary (useful when the shared boundary overlaps an
24397 // original boundary)
24398 int root_edge_bound_id = edge_boundary_id[ref_proc][counter];
24399 // Check if the shared boundary is overlapping (or is part)
24400 // of an internal boundary
24401 if (root_edge_bound_id != -1)
24402 {
24403 // If the shared boundary is part of an internal boundary then
24404 // mark the shared boundary
24405 this->Shared_boundary_overlaps_internal_boundary[shd_bnd_id] =
24406 static_cast<unsigned>(root_edge_bound_id);
24407 } // if (root_edge_bound_id != -1)
24408
24409 // Storing for the nodes of the polyline (these are different
24410 // from the nodes on the face elements -- it is actually a
24411 // sub-set -- since the polyline is created from the first and
24412 // last nodes on the face elements)
24413 Vector<Node*> node_pt_to_create_shared_polyline;
24414
24415 // Add the first node for the very first face element. In
24416 // the loop we will only add the last node of the face
24417 // element
24418 FiniteElement* first_face_ele_pt =
24419 sorted_face_ele_pt[ref_proc][counter][0];
24420
24421 // Get the number of nodes on the first face element
24422 const unsigned first_face_ele_nnodes = first_face_ele_pt->nnode();
24423 if (!is_inverted[first_face_ele_pt])
24424 {
24425 // Get the first node
24426 Node* first_node_pt = first_face_ele_pt->node_pt(0);
24427 // Add the node to create the polyline
24428 node_pt_to_create_shared_polyline.push_back(first_node_pt);
24429 // Add the first node to the shared boundary
24430 this->add_shared_boundary_node(shd_bnd_id, first_node_pt);
24431 }
24432 else
24433 {
24434 // Get the first node in the inverted face element
24435 Node* first_node_pt =
24436 first_face_ele_pt->node_pt(first_face_ele_nnodes - 1);
24437 // Add the node to create the polyline
24438 node_pt_to_create_shared_polyline.push_back(first_node_pt);
24439 // Add the first node to the shared boundary
24440 this->add_shared_boundary_node(shd_bnd_id, first_node_pt);
24441 }
24442
24443 // ... and extract only the last nodes of the face elements
24444 // in the next loop and add them in the vector of nodes to
24445 // create polylines (node_pt_to_create_shared_polyline)
24446
24447 // Get the number of elements
24448 const unsigned nshared_boundary_elements =
24449 sorted_face_ele_pt[ref_proc][counter].size();
24450
24451 // Store the shared boundary elements, nodes and get the
24452 // sorted nodes to create the polyline
24453 for (unsigned ie = 0; ie < nshared_boundary_elements; ie++)
24454 {
24455 // Get the bulk element version of the face element
24456 FiniteElement* bulk_ele_pt = sorted_ele_pt[ref_proc][counter][ie];
24457
24458 // Add the shared boundary element and associate it to the
24459 // "shd_bnd_id"
24460 this->add_shared_boundary_element(shd_bnd_id, bulk_ele_pt);
24461
24462 // Get the face index from which the face element was
24463 // created from the bulk element
24464 const int face_index =
24465 sorted_face_index_ele[ref_proc][counter][ie];
24466
24467 // Add the face index to the face indexes of the shared
24468 // boundary
24469 this->add_face_index_at_shared_boundary(shd_bnd_id, face_index);
24470
24471 // Get the face element to obtain the last node
24472 FiniteElement* face_ele_pt =
24473 sorted_face_ele_pt[ref_proc][counter][ie];
24474
24475 // Get the number of nodes
24476 const unsigned nnodes = face_ele_pt->nnode();
24477 if (!is_inverted[face_ele_pt])
24478 {
24479 // We have already added the first node, then start from
24480 // the second one
24481 for (unsigned n = 1; n < nnodes; n++)
24482 {
24483 // Get the node to be added
24484 Node* node_pt = face_ele_pt->node_pt(n);
24485 // Add the node and associate it to the shared boundary
24486 this->add_shared_boundary_node(shd_bnd_id, node_pt);
24487 } // for (n < nnodes)
24488
24489 // Add the last node of the face element to the vector of
24490 // nodes to create the polyline
24491 // Get the last node
24492 Node* last_node_pt = face_ele_pt->node_pt(nnodes - 1);
24493 node_pt_to_create_shared_polyline.push_back(last_node_pt);
24494 } // if (!is_inverted[face_ele_pt])
24495 else
24496 {
24497 // We have already added the first node, then start from
24498 // the second one (in reverse order)
24499 for (int n = nnodes - 2; n >= 0; n--)
24500 {
24501 // Get the node to be added
24502 Node* node_pt = face_ele_pt->node_pt(n);
24503 // Add the node and associate it to the shared boundary
24504 this->add_shared_boundary_node(shd_bnd_id, node_pt);
24505 } // for (n < nnodes)
24506
24507 // Add the last node of the face element to the vector of
24508 // nodes to create the polyline
24509 // Get the last node
24510 Node* last_node_pt = face_ele_pt->node_pt(0);
24511 node_pt_to_create_shared_polyline.push_back(last_node_pt);
24512
24513 } // else if (!is_inverted[face_ele_pt])
24514
24515 } // for (ie < nshared_boundary_elements)
24516
24517 // The number of nodes for the shared boundary polyline
24518 const unsigned nnodes_to_create_shared_boundary =
24519 node_pt_to_create_shared_polyline.size();
24520
24521 // Get the vertices that create the shared boundary polyline
24522 Vector<Vector<double>> vertices(nnodes_to_create_shared_boundary);
24523 for (unsigned n = 0; n < nnodes_to_create_shared_boundary; n++)
24524 {
24525 vertices[n].resize(2);
24526 // Get the node
24527 Node* tmp_node_pt = node_pt_to_create_shared_polyline[n];
24528 // Get the vertices
24529 vertices[n][0] = tmp_node_pt->x(0);
24530 vertices[n][1] = tmp_node_pt->x(1);
24531 } // for (n < nnodes_to_create_shared_boundary)
24532
24533 // Create the polyline
24534 TriangleMeshPolyLine* polyline_pt =
24535 new TriangleMeshPolyLine(vertices, shd_bnd_id);
24536
24537 // Updates bnd_id<--->curve section map
24538 this->Boundary_curve_section_pt[shd_bnd_id] = polyline_pt;
24539
24540 // Add the new created polyline to the list of unsorted
24541 // polylines
24542 unsorted_polylines_pt.push_back(polyline_pt);
24543
24544 // Mark the polyline for deletion (when calling destructor)
24545 this->Free_curve_section_pt.insert(polyline_pt);
24546
24547 // Now assign the connection information
24548 // ---------------------------------------------------------
24549 // Get the local shared boundary id associated to the
24550 // elements that gave rise to this shared boundary
24551 const unsigned local_shd_bnd_id =
24552 proc_local_shared_boundary_id[ref_proc][counter];
24553
24554 // Associate the local shared boundary to the global shared
24555 // boundary
24556 local_to_global_shd_bnd_id[local_shd_bnd_id] = shd_bnd_id;
24557
24558 // Get the correct shared boundaries ids, from the local
24559 // shared boundaries ids established at the identification
24560 // of the conections
24561
24562 // Get the local bnd id for the connection to the left
24563 int tmp_bnd_id_connection_to_the_left =
24564 sorted_connection_info[ref_proc][counter][0];
24565 // Get the local bnd id for the connection to the right
24566 int tmp_bnd_id_connection_to_the_right =
24567 sorted_connection_info[ref_proc][counter][1];
24568
24569 // The global shared boundaries ids for connections to the
24570 // left or right
24571 int bnd_id_connection_to_the_left = -1;
24572 int bnd_id_connection_to_the_right = -1;
24573
24574 // To the left
24575 // --------------
24576
24577 // If the connection is with the same shared boundary then
24578 // set the current boundary id
24579 if (tmp_bnd_id_connection_to_the_left == -2)
24580 {
24581 // Set the current shared boundary id
24582 bnd_id_connection_to_the_left = shd_bnd_id;
24583 } // if (tmp_bnd_id_connection_to_the_left == -2)
24584
24585 // Check if the connection was a stop adding nodes condition
24586 if (tmp_bnd_id_connection_to_the_left == -3)
24587 {
24588 // Set as no connected
24589 bnd_id_connection_to_the_left = -1;
24590 } // if (tmp_bnd_id_connection_to_the_left == -3)
24591
24592 // There is a connection with another boundary, check if it
24593 // is a shared boundary or an original boundary
24594 if (tmp_bnd_id_connection_to_the_left >=
24595 static_cast<int>(old_local_shd_bnd_id))
24596 {
24597 // The connection is with a shared boundary, get the
24598 // global shared boundary id and set the connection
24599#ifdef PARANOID
24600 std::map<unsigned, unsigned>::iterator it =
24601 local_to_global_shd_bnd_id.find(
24602 static_cast<unsigned>(tmp_bnd_id_connection_to_the_left));
24603 // If the global shared boundary id was not found we
24604 // are in trouble
24605 if (it == local_to_global_shd_bnd_id.end())
24606 {
24607 std::stringstream error_message;
24608 error_message
24609 << "The global shared boundary id was not found for\n"
24610 << "the local shared boundary shared with processor ("
24611 << ref_proc << ").\n"
24612 << "This processor: (" << my_rank << ")\n"
24613 << "Boundary shared with processor: (" << ref_proc << ")\n"
24614 << "Local shared boundary: ("
24615 << tmp_bnd_id_connection_to_the_left << ")\n";
24616 throw OomphLibError(error_message.str(),
24617 OOMPH_CURRENT_FUNCTION,
24618 OOMPH_EXCEPTION_LOCATION);
24619 } // if (it==local_to_global_shd_bnd_id.end())
24620#endif
24621
24622 // Get the global shared boundary id
24623 bnd_id_connection_to_the_left =
24624 local_to_global_shd_bnd_id[static_cast<unsigned>(
24625 tmp_bnd_id_connection_to_the_left)];
24626 }
24627 else
24628 {
24629 // The connection is with an original boundary, copy
24630 // the boundary id
24631 bnd_id_connection_to_the_left = tmp_bnd_id_connection_to_the_left;
24632
24633 } // else (connection with a shared boundary)
24634
24635 // To the right
24636 // --------------
24637
24638 // If the connection is with the same shared boundary then
24639 // set the current boundary id
24640 if (tmp_bnd_id_connection_to_the_right == -2)
24641 {
24642 // Set the current shared boundary id
24643 bnd_id_connection_to_the_right = shd_bnd_id;
24644 } // if (tmp_bnd_id_connection_to_the_right == -2)
24645
24646 // Check if the connection was a stop adding nodes condition
24647 if (tmp_bnd_id_connection_to_the_right == -3)
24648 {
24649 // Set as no connected
24650 bnd_id_connection_to_the_right = -1;
24651 } // if (tmp_bnd_id_connection_to_the_right == -3)
24652
24653 // There is a connection with another boundary, check if it
24654 // is a shared boundary or an original boundary
24655 if (tmp_bnd_id_connection_to_the_right >=
24656 static_cast<int>(old_local_shd_bnd_id))
24657 {
24658 // The connection is with a shared boundary, get the
24659 // global shared boundary id and set the connection
24660#ifdef PARANOID
24661 std::map<unsigned, unsigned>::iterator it =
24662 local_to_global_shd_bnd_id.find(
24663 static_cast<unsigned>(tmp_bnd_id_connection_to_the_right));
24664 // If the global shared boundary id was not found we
24665 // are in trouble
24666 if (it == local_to_global_shd_bnd_id.end())
24667 {
24668 std::stringstream error_message;
24669 error_message
24670 << "The global shared boundary id was not found for\n"
24671 << "the local shared boundary shared with processor ("
24672 << ref_proc << ").\n"
24673 << "This processor: (" << my_rank << ")\n"
24674 << "Boundary shared with processor: (" << ref_proc << ")\n"
24675 << "Local shared boundary: ("
24676 << tmp_bnd_id_connection_to_the_right << ")\n";
24677 throw OomphLibError(error_message.str(),
24678 OOMPH_CURRENT_FUNCTION,
24679 OOMPH_EXCEPTION_LOCATION);
24680 } // if (it==local_to_global_shd_bnd_id.end())
24681#endif
24682 // Get the global shared boundary id
24683 bnd_id_connection_to_the_right =
24684 local_to_global_shd_bnd_id[static_cast<unsigned>(
24685 tmp_bnd_id_connection_to_the_right)];
24686 }
24687 else
24688 {
24689 // The connection is with an original boundary, copy the
24690 // boundary id
24691 bnd_id_connection_to_the_right =
24692 tmp_bnd_id_connection_to_the_right;
24693
24694 } // else (connection with a shared boundary)
24695
24696 // --------------------------------
24697 // Set the connection to the left
24698 if (bnd_id_connection_to_the_left != -1)
24699 {
24700 // Get the unsigned version of the boundary id to the left
24701 const unsigned ubnd_id_connection_to_the_left =
24702 static_cast<unsigned>(bnd_id_connection_to_the_left);
24703 // Set the initial vertex as connected
24704 polyline_pt->set_initial_vertex_connected();
24705 // Set the initial vertex connected boundary id
24706 polyline_pt->initial_vertex_connected_bnd_id() =
24707 ubnd_id_connection_to_the_left;
24708 // Set the chunk number to zero
24709 polyline_pt->initial_vertex_connected_n_chunk() = 0;
24710
24711 } // if (bnd_id_connection_to_the_left != -1)
24712
24713 // ---------------------------------
24714 // Set the connection to the right
24715 if (bnd_id_connection_to_the_right != -1)
24716 {
24717 // Get the unsigned version of the boundary id to the
24718 // right
24719 const unsigned ubnd_id_connection_to_the_right =
24720 static_cast<unsigned>(bnd_id_connection_to_the_right);
24721 // Set the final vertex as connected
24722 polyline_pt->set_final_vertex_connected();
24723 // Set the final vertex connected boundary id
24724 polyline_pt->final_vertex_connected_bnd_id() =
24725 ubnd_id_connection_to_the_right;
24726 // Set the chunk number to zero
24727 polyline_pt->final_vertex_connected_n_chunk() = 0;
24728
24729 } // if (bnd_id_connection_to_the_right != -1)
24730
24731 } // for (counter < nshared_bound_iproc_jproc)
24732
24733 } // if (iproc == my_rank || jproc == my_rank)
24734 else
24735 {
24736 // We are not working with the current processor, then we only
24737 // need to fill the containers
24738
24739 // Get the number of shared boundaries between processor iproc
24740 // and processor jproc
24741 const unsigned nshared_bound_iproc_jproc =
24742 nshared_bound_proc_with_proc[iproc][jproc];
24743 // Loop over the number of shared boundaries
24744 for (unsigned counter = 0; counter < nshared_bound_iproc_jproc;
24745 counter++)
24746 {
24747 // Compute the shared boundary id for the shared boundary
24748 const unsigned shd_bnd_id =
24749 initial_shared_bound_id_proc_with_proc[iproc][jproc] + counter;
24750
24751 // Set up the shared boundaries between "iproc" and "jproc"
24752 this->Shared_boundaries_ids[iproc][jproc].push_back(shd_bnd_id);
24753 this->Shared_boundaries_ids[jproc][iproc].push_back(shd_bnd_id);
24754
24755 // Specify the processors involved for the creation of the
24756 // shared boundary
24757 Vector<unsigned> processors(2);
24758 processors[0] = iproc;
24759 processors[1] = jproc;
24760 this->Shared_boundary_from_processors[shd_bnd_id] = processors;
24761
24762 } // for (counter < nshared_bound_iproc_jproc)
24763
24764 } // else if (iproc == my_rank || jproc == my_rank)
24765
24766 } // for (jproc < nproc)
24767
24768 } // for (iproc < nproc)
24769
24770 // Get the time to create new shared boundaries representations
24771 if (Print_timings_level_load_balance > 2)
24772 {
24773 oomph_info << "CPU for creating new shared boundaries representations "
24774 "(load balance) [9.7]: "
24776 tt_start_create_new_shared_boundaries_polylines
24777 << std::endl;
24778 }
24779
24780 // ==================================================================
24781 // END: CREATE THE NEW SHARED BOUNDARIES. DELETE THE OLD SHARED
24782 // BOUNDARIES INFORMATION. FILL THE DATA STRUCTURES WITH THE NEW
24783 // SHARED BOUNDARIES INFO.
24784 // ==================================================================
24785
24786 // ==================================================================
24787 // BEGIN: SORT THE SHARED BOUNDARIES AND CREATE SHARED CURVES (A SET
24788 // OF CONTIGUOUS SHARED POLYLINES). STORE THEM IN THE GLOBAL
24789 // CONTAINER FOR SHARED BOUNDARIES. FREE MEMORY BY DELETING FACE
24790 // ELEMENTS
24791 // ==================================================================
24792
24793 // Get the time to create the new shared curves
24794 double tt_start_create_new_shared_curves = 0.0;
24795 if (Print_timings_level_load_balance > 2)
24796 {
24797 tt_start_create_new_shared_curves = TimingHelpers::timer();
24798 }
24799
24800 // Sort the polylines and find if they create a contiguous open
24801 // curve
24802 if (unsorted_polylines_pt.size() > 0)
24803 {
24804 // Now that we have all the new unsorted polylines on "my_rank"x
24805 // processor it is time to sort them so they be all contiguous
24806 this->sort_polylines_helper(unsorted_polylines_pt,
24807 this->Shared_boundary_polyline_pt[my_rank]);
24808 }
24809
24810 // Free the memory allocated for the face elements
24811 for (unsigned iproc = 0; iproc < nproc; iproc++)
24812 {
24813 const unsigned nface_ele = unsorted_face_ele_pt[iproc].size();
24814 for (unsigned e = 0; e < nface_ele; e++)
24815 {
24816 delete unsorted_face_ele_pt[iproc][e];
24817 unsorted_face_ele_pt[iproc][e] = 0;
24818 } // for (e < nface_ele)
24819
24820 } // for (iproc < nproc)
24821
24822 // The time to create the new shared curves
24823 if (Print_timings_level_load_balance > 2)
24824 {
24826 << "CPU for creating the new shared curves (load balance) [9.8]: "
24827 << TimingHelpers::timer() - tt_start_create_new_shared_curves
24828 << std::endl;
24829 }
24830
24831 // ==================================================================
24832 // END: SORT THE SHARED BOUNDARIES AND CREATE SHARED CURVES (A SET
24833 // OF CONTIGUOUS SHARED POLYLINES). STORE THEM IN THE GLOBAL
24834 // CONTAINER FOR SHARED BOUNDARIES. FREE MEMORY BY DELETING FACE
24835 // ELEMENTS
24836 // ==================================================================
24837 }
24838
24839 //======================================================================
24840 // Computes the degree of the nodes on the shared boundaries, the
24841 // degree of the node is computed from the global graph created by the
24842 // shared boundaries of all processors
24843 //======================================================================
24844 template<class ELEMENT>
24846 Vector<Vector<FiniteElement*>>& unsorted_face_ele_pt,
24847 std::map<Node*, unsigned>& global_node_degree)
24848 {
24849 // Get the rank and number of processors
24850 const unsigned nproc = this->communicator_pt()->nproc();
24851 const unsigned my_rank = this->communicator_pt()->my_rank();
24852
24853 // Store a temporary sorting of the nodes, starting from the
24854 // lower-left position
24855 Vector<Vector<Node*>> tmp_sorted_shared_node_pt(nproc);
24856
24857 // Store the alias of the node, it may be shared by more than two
24858 // processors, they should know that the node is the same
24859 // [0] iproc, processor with which the current processor shared the node
24860 // [1] node #, number of node in the number of nodes shared with iproc
24861 // processor
24862 std::map<Node*, Vector<Vector<unsigned>>> node_alias;
24863
24864 // Stores the local adjacency matrix
24865 // (nproc*n_shared_nodes*n_shared_nodes)
24866 Vector<Vector<Vector<unsigned>>> local_adjacency_matrix(nproc);
24867
24868 // Sort the nodes and create the adjacency matrix of each sub-graph
24869 // created by the shared edges
24870 create_adjacency_matrix_new_shared_edges_helper(unsorted_face_ele_pt,
24871 tmp_sorted_shared_node_pt,
24872 node_alias,
24873 local_adjacency_matrix);
24874
24875 // Prepare the info. to be sent to the root processor, which will be
24876 // in charge of updating the nodes degree by combining the info. of
24877 // all the processors
24878
24879 // The flat package with the info. to send to root
24880 Vector<unsigned> package_unsigned_send_data_to_root;
24881
24882 // Encode the info. that will be sent to the root processor
24883
24884 // Loop over the temporary sorted nodes between each pair of
24885 // processors
24886 for (unsigned iproc = 0; iproc < nproc; iproc++)
24887 {
24888 // Send the processor index
24889 package_unsigned_send_data_to_root.push_back(iproc);
24890
24891 // Get the number of nodes shared between the processors
24892 const unsigned n_nodes = tmp_sorted_shared_node_pt[iproc].size();
24893
24894 // Send the number of nodes shared with the iproc processor
24895 package_unsigned_send_data_to_root.push_back(n_nodes);
24896
24897 // Loop over the nodes
24898 for (unsigned ishd = 0; ishd < n_nodes; ishd++)
24899 {
24900 // Get the node
24901 Node* shd_node_pt = tmp_sorted_shared_node_pt[iproc][ishd];
24902
24903 // Get the alias info.
24904 Vector<Vector<unsigned>> alias_node_info = node_alias[shd_node_pt];
24905
24906 // Get the number of alias for the node
24907 const unsigned n_alias = alias_node_info.size();
24908
24909 // Send the number of alias assigned to the node
24910 package_unsigned_send_data_to_root.push_back(n_alias);
24911
24912 // Loop over the alias to include them in the package
24913 for (unsigned i = 0; i < n_alias; i++)
24914 {
24915 // Send the alias info.
24916 // The current processor
24917 package_unsigned_send_data_to_root.push_back(alias_node_info[i][0]);
24918 // The prociesso with which is shared
24919 package_unsigned_send_data_to_root.push_back(alias_node_info[i][1]);
24920 // The index of the node
24921 package_unsigned_send_data_to_root.push_back(alias_node_info[i][2]);
24922 } // for (i < n_alias)
24923
24924 } // for (ishd < n_nodes)
24925
24926 // Now send the adjacency matrix
24927 for (unsigned i = 0; i < n_nodes; i++)
24928 {
24929 for (unsigned j = 0; j < n_nodes; j++)
24930 {
24931 // Package the adjacency matrix
24932 package_unsigned_send_data_to_root.push_back(
24933 local_adjacency_matrix[iproc][i][j]);
24934
24935 } // for (j < n_nodes)
24936
24937 } // for (i < n_nodes)
24938
24939 } // for (iproc < nproc)
24940
24941 // Define the root processor
24942 const unsigned root_processor = 0;
24943
24944 // Get the communicator of the mesh
24945 OomphCommunicator* comm_pt = this->communicator_pt();
24946
24947 // Number of data send. from this processor to root processor
24948 unsigned n_unsigned_data_send_to_root =
24949 package_unsigned_send_data_to_root.size();
24950
24951 // Store the number of data to receive from each processor in root
24952 Vector<int> n_unsigned_data_received_in_root(nproc, 0);
24953
24954 // Send the number of data that each processor will send to root
24955 // Gather the info. in the "root_processor"
24956 MPI_Gather(&n_unsigned_data_send_to_root, // Info. sent from
24957 // each processor
24958 1, // Total number of data to send from each processor
24959 MPI_UNSIGNED,
24960 &n_unsigned_data_received_in_root[0], // Container where
24961 // to receive the
24962 // info. from all
24963 // the processors
24964 1, // Number of data to receive from each processor
24965 MPI_UNSIGNED,
24966 root_processor, // The processor that receives all the
24967 // info.
24968 comm_pt->mpi_comm());
24969
24970 // Compute the total number of data to receive from all processors
24971 unsigned n_unsigned_total_data_receive_in_root = 0;
24972 for (unsigned iproc = 0; iproc < nproc; iproc++)
24973 {
24974 // Add the number of data to receive from each processor
24975 n_unsigned_total_data_receive_in_root +=
24976 n_unsigned_data_received_in_root[iproc];
24977 }
24978
24979 // Compute the offsets from each processor
24980 Vector<int> root_unsigned_offsets_receive(nproc, 0);
24981 root_unsigned_offsets_receive[0] = 0;
24982 for (unsigned iproc = 1; iproc < nproc; iproc++)
24983 {
24984 // Compute the offset to store the values received from each
24985 // processor
24986 root_unsigned_offsets_receive[iproc] =
24987 root_unsigned_offsets_receive[iproc - 1] +
24988 n_unsigned_data_received_in_root[iproc - 1];
24989 }
24990
24991 // Create at least one entry so we don't get a seg fault below
24992 if (package_unsigned_send_data_to_root.size() == 0)
24993 {
24994 package_unsigned_send_data_to_root.resize(1);
24995 }
24996
24997 // Vector where to receive the data sent from each processor
24998 Vector<unsigned> package_unsigned_data_received_root(
24999 n_unsigned_total_data_receive_in_root);
25000 if (my_rank != root_processor)
25001 {
25002 // Create at least one entry so we don't get a seg fault below
25003 if (package_unsigned_data_received_root.size() == 0)
25004 {
25005 package_unsigned_data_received_root.resize(1);
25006 }
25007 } // if (my_rank!=root_processor)
25008
25009 // Gather the info. from all processors
25010 MPI_Gatherv(&package_unsigned_send_data_to_root[0], // Flat package
25011 // to send
25012 // info. from
25013 // each
25014 // processor
25015 n_unsigned_data_send_to_root, // Total number of data to
25016 // send from each
25017 // processor
25018 MPI_UNSIGNED,
25019 &package_unsigned_data_received_root[0], // Container
25020 // where to
25021 // receive the
25022 // info. from
25023 // all the
25024 // processors
25025 &n_unsigned_data_received_in_root[0], // Number of data
25026 // to receive from
25027 // each processor
25028 &root_unsigned_offsets_receive[0], // The offset to
25029 // store the
25030 // info. from each
25031 // processor
25032 MPI_UNSIGNED,
25033 root_processor, // The processor that receives all the
25034 // info.
25035 comm_pt->mpi_comm());
25036
25037 // Store the info. to be sent by root to other processors
25038 Vector<unsigned> package_unsigned_data_sent_from_root;
25039 // Total data sent to each processor from root
25040 Vector<int> n_unsigned_data_sent_from_root(nproc, 0);
25041
25042 // The root processor now has all the info. regarding the shared
25043 // nodes and the adjacency matrix of each pair of processors
25044 if (my_rank == root_processor)
25045 {
25046 // Decode the info. received from all processors
25047 // Counter to decode the info.
25048 unsigned decode_counter = 0;
25049
25050 // Store the local alias of the nodes in each processor
25051 // [x][][][][] iproc
25052 // [][x][][][] jproc
25053 // [][][x][][] inode
25054 // [][][][x][] ialias
25055 // [][][][][x] alias_data
25056 Vector<Vector<Vector<Vector<Vector<unsigned>>>>> local_node_alias(nproc);
25057 // Store the local adjacency matrix of each processor
25058 Vector<Vector<Vector<Vector<unsigned>>>> local_adjacency_matrix(nproc);
25059
25060 // Loop over all the processors
25061 for (unsigned iproc = 0; iproc < nproc; iproc++)
25062 {
25063 local_node_alias[iproc].resize(nproc);
25064
25065 // Resize the local adjacency matrix to store the info. sent
25066 // from all processors
25067 local_adjacency_matrix[iproc].resize(nproc);
25068
25069 if (n_unsigned_data_received_in_root[iproc] > 0)
25070 {
25071 // Loop over all the processors to decode the info. received
25072 // from each one
25073 for (unsigned jproc = 0; jproc < nproc; jproc++)
25074 {
25075 // Read the processor number to which the info. correspond
25076 const unsigned read_jproc =
25077 package_unsigned_data_received_root[decode_counter++];
25078
25079 // The read processor must be the same as the jproc, if that
25080 // is not the case then there is a synchronisation issue
25081 if (read_jproc != jproc)
25082 {
25083 std::ostringstream error_stream;
25084 error_stream
25085 << "The read processor is different from the jproc, this is\n"
25086 << "a synchronisation issue. The data are not read in the\n"
25087 << "sameorder as the were packaged\n"
25088 << "Read processor: (" << read_jproc << ")\n"
25089 << "Current jproc: (" << jproc << ")\n\n";
25090 throw OomphLibError(
25091 error_stream.str(),
25092 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25093 OOMPH_EXCEPTION_LOCATION);
25094 }
25095
25096 // Read the number of nodes in the shared boundaries between
25097 // iproc and jproc
25098 const unsigned read_n_shd_nodes_iproc_jproc =
25099 package_unsigned_data_received_root[decode_counter++];
25100
25101 // Resize the container
25102 local_node_alias[iproc][jproc].resize(read_n_shd_nodes_iproc_jproc);
25103
25104 // Loop over the number of nodes shared between iproc and
25105 // jproc
25106 for (unsigned ishd = 0; ishd < read_n_shd_nodes_iproc_jproc; ishd++)
25107 {
25108 // Read the number of alias of the current ishd node
25109 const unsigned read_n_alias_node_iproc_jproc =
25110 package_unsigned_data_received_root[decode_counter++];
25111
25112 // Resize the container
25113 local_node_alias[iproc][jproc][ishd].resize(
25114 read_n_alias_node_iproc_jproc);
25115
25116 for (unsigned ialias = 0; ialias < read_n_alias_node_iproc_jproc;
25117 ialias++)
25118 {
25119 // Resize the container, we know there are three data to
25120 // define the alias of a node
25121 local_node_alias[iproc][jproc][ishd][ialias].resize(3);
25122
25123 // The 1st processor with which is shared
25124 local_node_alias[iproc][jproc][ishd][ialias][0] =
25125 package_unsigned_data_received_root[decode_counter++];
25126
25127 // The 2nd processor with which is shared
25128 local_node_alias[iproc][jproc][ishd][ialias][1] =
25129 package_unsigned_data_received_root[decode_counter++];
25130
25131 // The index of the node in the interaction iproc-jproc
25132 local_node_alias[iproc][jproc][ishd][ialias][2] =
25133 package_unsigned_data_received_root[decode_counter++];
25134
25135 } // for (ialias < read_n_alias_node_iproc_jproc)
25136
25137 } // for (ishd < read_n_shd_nodes_iproc_jproc)
25138
25139 // Resize the local adjacency matrix
25140 local_adjacency_matrix[iproc][jproc].resize(
25141 read_n_shd_nodes_iproc_jproc);
25142 // Read the adjacency matrix sent to root processor
25143 for (unsigned i = 0; i < read_n_shd_nodes_iproc_jproc; i++)
25144 {
25145 // Resize the local adjacency matrix
25146 local_adjacency_matrix[iproc][jproc][i].resize(
25147 read_n_shd_nodes_iproc_jproc);
25148 for (unsigned j = 0; j < read_n_shd_nodes_iproc_jproc; j++)
25149 {
25150 // Read the adjacency matrix entry
25151 local_adjacency_matrix[iproc][jproc][i][j] =
25152 package_unsigned_data_received_root[decode_counter++];
25153 } // for (j < read_n_shd_nodes_iproc_jproc)
25154
25155 } // for (i < read_n_shd_nodes_iproc_jproc)
25156
25157 } // for (jproc < nproc)
25158
25159 } // for (iproc < nproc)
25160
25161 } // for (iproc < nproc)
25162
25163#ifdef PARANOID
25164 if (decode_counter != n_unsigned_total_data_receive_in_root)
25165 {
25166 std::ostringstream error_stream;
25167 error_stream
25168 << "The number of data decoded in root received from others\n"
25169 << "processors is different from the total number of data received\n"
25170 << "Data decoded: (" << decode_counter << ")\n"
25171 << "Data received: (" << n_unsigned_total_data_receive_in_root
25172 << ")\n\n"
25173 << "This is a synchronisation issue so you are probably sending\n"
25174 << "more or less info. than the one that is being decoded\n\n";
25175 throw OomphLibError(
25176 error_stream.str(),
25177 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25178 OOMPH_EXCEPTION_LOCATION);
25179 }
25180#endif
25181
25182 // Assign a unique id to the nodes (uses the alias information to
25183 // identify the repetition of a node in other processors). The
25184 // global node id is given by the position (index) in the global
25185 // node alias
25186
25187 // Keep track of those alias already assigned a unique id
25188 std::map<Vector<unsigned>, bool> alias_done;
25189
25190 // Store all the alias associated to each node
25191 Vector<Vector<Vector<unsigned>>> global_node_alias;
25192
25193 // Loop over all the processors
25194 for (unsigned iproc = 0; iproc < nproc; iproc++)
25195 {
25196 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
25197 {
25198 // Read the number of nodes shared between the processors
25199 const unsigned n_shd_nodes_iproc_jproc =
25200 local_node_alias[iproc][jproc].size();
25201#ifdef PARANOID
25202 // Read the number of nodes shared in the other direction
25203 const unsigned n_shd_nodes_jproc_iproc =
25204 local_node_alias[jproc][iproc].size();
25205
25206 if (n_shd_nodes_iproc_jproc != n_shd_nodes_jproc_iproc)
25207 {
25208 std::ostringstream error_stream;
25209 error_stream
25210 << "The number of nodes shared between iproc and jproc is\n"
25211 << "different from the number of nodes shared between jproc\n"
25212 << "and iproc\n"
25213 << "Nodes shared between processor (" << iproc << ") and "
25214 << "processor (" << jproc << "): (" << n_shd_nodes_iproc_jproc
25215 << ")\n"
25216 << "Nodes shared between processor (" << jproc << ") and "
25217 << "processor (" << iproc << "): (" << n_shd_nodes_jproc_iproc
25218 << ")\n\n";
25219 throw OomphLibError(
25220 error_stream.str(),
25221 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25222 OOMPH_EXCEPTION_LOCATION);
25223 } // if (n_shd_nodes_iproc_jproc != n_shd_nodes_jproc_iproc)
25224#endif
25225
25226 // Loop over the nodes shared between the processors
25227 for (unsigned ishd = 0; ishd < n_shd_nodes_iproc_jproc; ishd++)
25228 {
25229 // Get the number of alias associated to the node on each
25230 // processor
25231 const unsigned n_alias_iproc_jproc =
25232 local_node_alias[iproc][jproc][ishd].size();
25233 const unsigned n_alias_jproc_iproc =
25234 local_node_alias[jproc][iproc][ishd].size();
25235
25236 // Store all the found alias to the node
25237 Vector<Vector<unsigned>> node_alias;
25238
25239 // Flag to indicate if a new alias has been added
25240 bool new_alias_added = false;
25241
25242 // Start by adding the "direct" alias of the node
25243 for (unsigned ialias = 0; ialias < n_alias_iproc_jproc; ialias++)
25244 {
25245 // Get the alias of the node
25246 Vector<unsigned> current_alias =
25247 local_node_alias[iproc][jproc][ishd][ialias];
25248 // Check if already done
25249 if (!alias_done[current_alias])
25250 {
25251 // Add the alias of the node
25252 node_alias.push_back(current_alias);
25253 // Set the flag to indicate a new alias has been added
25254 new_alias_added = true;
25255 // Mark the alias as done
25256 alias_done[current_alias] = true;
25257 } // if (!alias_done[i_alias])
25258
25259 } // for (ialias < n_alias_iproc_jproc)
25260
25261 // Start by adding the "direct" alias of the node
25262 for (unsigned ialias = 0; ialias < n_alias_jproc_iproc; ialias++)
25263 {
25264 // Get the alias of the node
25265 Vector<unsigned> current_alias =
25266 local_node_alias[jproc][iproc][ishd][ialias];
25267
25268 // Check if already done
25269 if (!alias_done[current_alias])
25270 {
25271 // Add the alias of the node
25272 node_alias.push_back(current_alias);
25273 // Set the flag to indicate a new alias has been added
25274 new_alias_added = true;
25275 // Mark the alias as done
25276 alias_done[current_alias] = true;
25277 } // if (!alias_done[i_alias])
25278
25279 } // for (ialias < n_alias_jproc_iproc)
25280
25281 unsigned counter_alias = 0;
25282 // Visit the alias of the node and add any new found
25283 // alias, end until all its alias have been included
25284
25285 unsigned n_current_alias = node_alias.size();
25286 while (new_alias_added || counter_alias < n_current_alias)
25287 // while(new_alias_added) // we need to check all the alias,
25288 // including those added during the process
25289 {
25290 new_alias_added = false;
25291 // Store the current visited alias
25292 Vector<unsigned> current_alias = node_alias[counter_alias];
25293
25294 // Get the alias associated with the current alias
25295 Vector<Vector<unsigned>> alias_of_current_alias =
25296 local_node_alias[current_alias[0]][current_alias[1]]
25297 [current_alias[2]];
25298
25299 // Get all the alias associated with the alias of the
25300 // current alias
25301 const unsigned n_alias = alias_of_current_alias.size();
25302
25303 // Loop over the new alias and check if require to add
25304 // them
25305 for (unsigned k = 0; k < n_alias; k++)
25306 {
25307 // Get the alias of the node
25308 Vector<unsigned> add_alias = alias_of_current_alias[k];
25309
25310 // Check if already done
25311 if (!alias_done[add_alias])
25312 {
25313 // Add the alias of the node
25314 node_alias.push_back(add_alias);
25315 // Set the flag to indicate a new alias has been
25316 // added
25317 new_alias_added = true;
25318 // Mark the alias ad done
25319 alias_done[add_alias] = true;
25320 } // if (!alias_done[i_alias])
25321
25322 } // for (k < n_alias)
25323
25324 // Get the alias associated with the current alias (in the
25325 // other direction)
25326 Vector<Vector<unsigned>> alias_of_current_alias2 =
25327 local_node_alias[current_alias[1]][current_alias[0]]
25328 [current_alias[2]];
25329
25330 // Get all the alias associated with the current alias
25331 // (in the other direction)
25332 const unsigned n_alias2 = alias_of_current_alias2.size();
25333
25334 // Loop over the new alias and check if require to add
25335 // them
25336 for (unsigned k = 0; k < n_alias2; k++)
25337 {
25338 // Get the alias of the node
25339 Vector<unsigned> add_alias = alias_of_current_alias2[k];
25340
25341 // Check if already done
25342 if (!alias_done[add_alias])
25343 {
25344 // Add the alias of the node
25345 node_alias.push_back(add_alias);
25346 // Set the flag to indicate a new alias has been
25347 // added
25348 new_alias_added = true;
25349 // Mark the alias ad done
25350 alias_done[add_alias] = true;
25351 } // if (!alias_done[i_alias])
25352
25353 } // for (k < n_alias)
25354
25355 // Go for the next alias
25356 counter_alias++;
25357
25358 // Update the number of alias so that the while stops when
25359 // all the alias have been visited and no new alias was
25360 // added
25361 n_current_alias = node_alias.size();
25362
25363 } // while(new_alias_added || counter_alias < n_current_alias)
25364
25365 // If the node has not been previously added, then include
25366 // all its alias
25367 if (node_alias.size() > 0)
25368 {
25369 // Add all the found alias of the node to the global alias
25370 // storage
25371 global_node_alias.push_back(node_alias);
25372 }
25373
25374 } // for (ishd < n_shd_nodes_iproc_jproc)
25375
25376 } // for (jproc < nproc)
25377
25378 } // for (iproc < nproc)
25379
25380 // We now have the global number of nodes, each with its own id
25381 // (the index in the global_node_alias vector)
25382
25383 // Get the number of global shared nodes
25384 const unsigned n_global_shared_nodes = global_node_alias.size();
25385
25386 // Create matrix from local to global shared node id
25387 Vector<Vector<Vector<int>>> local_to_global_shared_node(nproc);
25388
25389 // Loop over all the processors to resize
25390 for (unsigned iproc = 0; iproc < nproc; iproc++)
25391 {
25392 // Resize the map matrix
25393 local_to_global_shared_node[iproc].resize(nproc);
25394 } // for (iproc < nproc)
25395
25396 // Loop over all the processors to resize (the third direction,
25397 // required if we want to loop over the half of the matrix only)
25398 for (unsigned iproc = 0; iproc < nproc; iproc++)
25399 {
25400 // Loop over the half of the matrix to resize
25401 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
25402 {
25403 // Read the number of nodes shared between the processors
25404 const unsigned n_shd_nodes = local_node_alias[iproc][jproc].size();
25405
25406 // Resize the map matrix
25407 local_to_global_shared_node[iproc][jproc].resize(n_shd_nodes, -1);
25408
25409 // ... and resize the other half map matrix
25410 local_to_global_shared_node[jproc][iproc].resize(n_shd_nodes, -1);
25411
25412 } // for (jproc < nproc)
25413
25414 } // for (iproc < nproc)
25415
25416 // Fill the matrix for mapping from local to global node id
25417
25418 // Loop over the global nodes, and for each alias assign the
25419 // corresponding global node id
25420 for (unsigned k = 0; k < n_global_shared_nodes; k++)
25421 {
25422 // Get the number of alias associated to the current global node
25423 const unsigned n_alias_global_node = global_node_alias[k].size();
25424 // Loop over the alias and assign the global node id
25425 for (unsigned l = 0; l < n_alias_global_node; l++)
25426 {
25427 // Get the 1st processor
25428 const unsigned iproc = global_node_alias[k][l][0];
25429 // Get the 2nd processor
25430 const unsigned jproc = global_node_alias[k][l][1];
25431 // Get the node number
25432 const unsigned ishd = global_node_alias[k][l][2];
25433 // Assign the global node id
25434 local_to_global_shared_node[iproc][jproc][ishd] = k;
25435
25436 } // for (l < n_alias_global_node)
25437
25438 } // for (k < n_global_shared_nodes)
25439
25440 // Create the global adjacency matrix
25441 Vector<Vector<unsigned>> global_adjacency_matrix(n_global_shared_nodes);
25442 // Resize the global adjacency matrix
25443 for (unsigned k = 0; k < n_global_shared_nodes; k++)
25444 {
25445 // Resize
25446 global_adjacency_matrix[k].resize(n_global_shared_nodes, 0);
25447 } // for (k < n_global_shared_nodes)
25448
25449 // Add the entries to the global adjacency matrix and compute the
25450 // degree of each node
25451
25452 // Store the degree of the global nodes
25453 Vector<unsigned> global_node_degree(n_global_shared_nodes, 0);
25454
25455 // Loop over the processors
25456 for (unsigned iproc = 0; iproc < nproc; iproc++)
25457 {
25458 // Loop over the half of the matrix to resize
25459 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
25460 {
25461 // Get the number of nodes shared between the processors
25462 const unsigned n_shd_nodes = local_node_alias[iproc][jproc].size();
25463
25464 // Search for entries in the local adjacency matrix that set a
25465 // connection among the nodes
25466
25467 // Loop over the shared nodes in the current pair of
25468 // processors
25469 for (unsigned ishd = 0; ishd < n_shd_nodes; ishd++)
25470 {
25471 for (unsigned jshd = ishd + 1; jshd < n_shd_nodes; jshd++)
25472 {
25473 // Are the nodes associated
25474 if (local_adjacency_matrix[iproc][jproc][ishd][jshd] > 0)
25475 {
25476 // Get the global nodes id
25477
25478 // Get the "left-node" global id
25479 const int global_shd_node_left =
25480 local_to_global_shared_node[iproc][jproc][ishd];
25481
25482 // Get the "right-node" global id
25483 const int global_shd_node_right =
25484 local_to_global_shared_node[iproc][jproc][jshd];
25485
25486#ifdef PARANOID
25487 // Check if the local nodes have a global node
25488 // associated
25489 if (global_shd_node_left == -1)
25490 {
25491 std::ostringstream error_stream;
25492 error_stream
25493 << "The local node in processors iproc and jproc has no\n"
25494 << "global node assigned\n"
25495 << "iproc processor: (" << iproc << ")\n"
25496 << "jproc processor: (" << jproc << ")\n"
25497 << "Local node: (" << ishd << ")\n\n";
25498 throw OomphLibError(error_stream.str(),
25499 "RefineableTriangleMesh::compute_shared_"
25500 "node_degree_helper()",
25501 OOMPH_EXCEPTION_LOCATION);
25502 }
25503
25504 // Check if the local nodes have a global node
25505 // associated
25506 if (global_shd_node_right == -1)
25507 {
25508 std::ostringstream error_stream;
25509 error_stream
25510 << "The local node in processors iproc and jproc has no\n"
25511 << "global node assigned\n"
25512 << "iproc processor: (" << iproc << ")\n"
25513 << "jproc processor: (" << jproc << ")\n"
25514 << "Local node: (" << jshd << ")\n\n";
25515 throw OomphLibError(error_stream.str(),
25516 "RefineableTriangleMesh::compute_shared_"
25517 "node_degree_helper()",
25518 OOMPH_EXCEPTION_LOCATION);
25519 }
25520#endif
25521 // Get the unsigned version of the indexes
25522 const unsigned uleft =
25523 static_cast<unsigned>(global_shd_node_left);
25524 const unsigned uright =
25525 static_cast<unsigned>(global_shd_node_right);
25526
25527 // Add the entry in the global adjacency matrix
25528 global_adjacency_matrix[uleft][uright]++;
25529
25530 // ... and in the other direction too
25531 global_adjacency_matrix[uright][uleft]++;
25532
25533 // Add on to the degree of the left node
25534 global_node_degree[uleft]++;
25535
25536 // Add on to the degree of the right node
25537 global_node_degree[uright]++;
25538
25539 } // if (local_adjacency_matrix[iproc][jproc][ishd][jshd] > 0)
25540
25541 } // // for (jshd < n_shd_nodes)
25542
25543 } // for (ishd < n_shd_nodes)
25544
25545 } // for (jproc < nproc)
25546
25547 } // for (iproc < nproc)
25548
25549 // Assign the global degree to the shared nodes between each pair
25550 // of processors
25551 Vector<Vector<Vector<unsigned>>> root_local_node_degree(nproc);
25552 // Resize the container
25553 for (unsigned iproc = 0; iproc < nproc; iproc++)
25554 {
25555 root_local_node_degree[iproc].resize(nproc);
25556 }
25557
25558 // Loop over the processors and visited their shared nodes
25559 for (unsigned iproc = 0; iproc < nproc; iproc++)
25560 {
25561 // Only visit the half of the data
25562 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
25563 {
25564 // Get the number of shared nodes between this pair of
25565 // processors (iproc, jproc)
25566 const unsigned n_shd_nodes = local_node_alias[iproc][jproc].size();
25567
25568 // Resize the container to store the local degree of the nodes
25569 root_local_node_degree[iproc][jproc].resize(n_shd_nodes);
25570 // ... and in the other way too
25571 root_local_node_degree[jproc][iproc].resize(n_shd_nodes);
25572
25573 // Loop over the number of nodes shared between the pair of
25574 // processors
25575 for (unsigned ishd = 0; ishd < n_shd_nodes; ishd++)
25576 {
25577 // Get the global node id for the current shared node
25578 const int global_shd_node_id =
25579 local_to_global_shared_node[iproc][jproc][ishd];
25580
25581#ifdef PARANOID
25582 // Check if the local nodes have a global node associated
25583 if (global_shd_node_id == -1)
25584 {
25585 std::ostringstream error_stream;
25586 error_stream
25587 << "The local node in processors iproc and jproc has no\n"
25588 << "global node assigned\n"
25589 << "iproc processor: (" << iproc << ")\n"
25590 << "jproc processor: (" << jproc << ")\n"
25591 << "Local node: (" << ishd << ")\n\n";
25592 throw OomphLibError(
25593 error_stream.str(),
25594 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25595 OOMPH_EXCEPTION_LOCATION);
25596 }
25597#endif
25598
25599 // Get the unsigned version of the global index
25600 const unsigned uglobal_shd_node_id =
25601 static_cast<unsigned>(global_shd_node_id);
25602
25603 // Get the degree of the node
25604 const unsigned node_degree =
25605 global_node_degree[uglobal_shd_node_id];
25606
25607 // Set the degree in the container for the degree of the
25608 // nodes in the local interaction between processors
25609 root_local_node_degree[iproc][jproc][ishd] = node_degree;
25610 // ... and in the other way too
25611 root_local_node_degree[jproc][iproc][ishd] = node_degree;
25612
25613 } // for (ishd < n_shd_nodes)
25614
25615 } // for (jproc < nproc)
25616
25617 } // for (iproc < nproc)
25618
25619 // Clear the container where the info. will be sent back to each
25620 // processor
25621 package_unsigned_data_sent_from_root.clear();
25622
25623 // Prepare the data to sent it back to each processor (encode the
25624 // info. to sent to all processors)
25625 for (unsigned iproc = 0; iproc < nproc; iproc++)
25626 {
25627 // Count the number of data sent to iproc processor
25628 unsigned count_n_data_sent_to_iproc = 0;
25629 for (unsigned jproc = 0; jproc < nproc; jproc++)
25630 {
25631 // No shared nodes between the same processor
25632 if (iproc != jproc)
25633 {
25634 // Get the number of nodes shared between the processors
25635 const unsigned n_shd_nodes =
25636 root_local_node_degree[iproc][jproc].size();
25637
25638 // Add the number of data sent to iproc processor
25639 count_n_data_sent_to_iproc += n_shd_nodes;
25640
25641 // Loop over the nodes shared between the pair of processors
25642 for (unsigned ishd = 0; ishd < n_shd_nodes; ishd++)
25643 {
25644 package_unsigned_data_sent_from_root.push_back(
25645 root_local_node_degree[iproc][jproc][ishd]);
25646 } // for (ishd < n_shd_nodes)
25647
25648 } // if (iproc != jproc)
25649
25650 } // for (jproc < nproc)
25651
25652 // Set the number of data sent to iproc processor
25653 n_unsigned_data_sent_from_root[iproc] = count_n_data_sent_to_iproc;
25654
25655 } // for (iproc < nproc)
25656
25657 } // if (my_rank == root_processor)
25658
25659 // Total data received from root to this processor
25660 int n_unsigned_data_received_from_root = 0;
25661
25662 // Get the number of data that each processor receives from root
25663 MPI_Scatter(&n_unsigned_data_sent_from_root[0], // Info. sent from
25664 // root to each
25665 // processor
25666 1, // The number of data sent from root to each
25667 // processor
25668 MPI_UNSIGNED,
25669 &n_unsigned_data_received_from_root, // Store the
25670 // info. received
25671 // from root
25672 1, // The number of data received from root
25673 MPI_UNSIGNED,
25674 root_processor, // The processor that sends the
25675 // info.
25676 comm_pt->mpi_comm());
25677
25678 // Receive the info. sent by root
25679 Vector<unsigned> package_unsigned_data_received_from_root(
25680 n_unsigned_data_received_from_root);
25681
25682 // Compute the offsets to each processor
25683 Vector<int> root_unsigned_offsets_sent(nproc, 0);
25684 root_unsigned_offsets_sent[0] = 0;
25685 for (unsigned iproc = 1; iproc < nproc; iproc++)
25686 {
25687 // Compute the offset to send the values to each processor
25688 root_unsigned_offsets_sent[iproc] =
25689 root_unsigned_offsets_sent[iproc - 1] +
25690 n_unsigned_data_sent_from_root[iproc - 1];
25691 }
25692
25693 if (my_rank != root_processor)
25694 {
25695 // Create at least one entry so we don't get a seg fault below
25696 if (package_unsigned_data_sent_from_root.size() == 0)
25697 {
25698 package_unsigned_data_sent_from_root.resize(1);
25699 }
25700 } // if (my_rank!=root_processor)
25701
25702 // Create at least one entry so we don't get a seg fault below
25703 if (package_unsigned_data_received_from_root.size() == 0)
25704 {
25705 package_unsigned_data_received_from_root.resize(1);
25706 }
25707
25708 // Get the data from root
25709 MPI_Scatterv(&package_unsigned_data_sent_from_root[0], // The
25710 // info. sent
25711 // from root
25712 // to others
25713 // processors
25714 &n_unsigned_data_sent_from_root[0], // The number of
25715 // data sent from
25716 // root to others
25717 // processors
25718 &root_unsigned_offsets_sent[0], // The offsets to each
25719 // processors
25720 MPI_UNSIGNED,
25721 &package_unsigned_data_received_from_root[0], // The
25722 // storage
25723 // in the
25724 // processor
25725 // that
25726 // receives
25727 // the
25728 // info.
25729 n_unsigned_data_received_from_root, // The number of
25730 // data that the
25731 // current
25732 // processor
25733 // receives from
25734 // root
25735 MPI_UNSIGNED,
25736 root_processor, // The root processors
25737 comm_pt->mpi_comm());
25738
25739 // Decode the info.
25740
25741 // Keep track of the already nodes done
25742 std::map<Node*, bool> node_done;
25743
25744 // Read the global degree assigned to the shared nodes between the
25745 // current processors and the other processors
25746 int decode_counter = 0;
25747 // Store the global degree of the local nodes
25748 Vector<Vector<unsigned>> local_node_degree(nproc);
25749 // Loop over the processors
25750 for (unsigned iproc = 0; iproc < nproc; iproc++)
25751 {
25752 // There are no shared nodes with the current processor itself
25753 if (iproc != my_rank)
25754 {
25755 // Get the number of nodes shared with the iproc processor
25756 const unsigned n_nodes = tmp_sorted_shared_node_pt[iproc].size();
25757
25758 // Read the global degree of the node
25759 package_unsigned_send_data_to_root.push_back(n_nodes);
25760
25761 // Loop over the nodes
25762 for (unsigned ishd = 0; ishd < n_nodes; ishd++)
25763 {
25764 // Get the node degree assigned to the ishd node in between
25765 // the interaction of the iproc and the current processor
25766 const unsigned node_degree =
25767 package_unsigned_data_received_from_root[decode_counter++];
25768
25769 // Get the node
25770 Node* shd_node_pt = tmp_sorted_shared_node_pt[iproc][ishd];
25771
25772 // Has the node been assigned a global degree
25773 if (!node_done[shd_node_pt])
25774 {
25775 // Assign the global degree to the node
25776 global_node_degree[shd_node_pt] = node_degree;
25777 // Mark the node as done
25778 node_done[shd_node_pt] = true;
25779 }
25780#ifdef PARANOID
25781 else
25782 {
25783 // The node has been already done, check that the node
25784 // degree is the same as the already assigned
25785 if (global_node_degree[shd_node_pt] != node_degree)
25786 {
25787 std::ostringstream error_stream;
25788 error_stream
25789 << "The local node has already assigned a global degree,\n"
25790 << "however, a different degree for the same node has been\n"
25791 << "read from the data sent from root processor\n"
25792 << "iproc processor: (" << iproc << ")\n"
25793 << "Local node: (" << ishd << ")\n"
25794 << "---------------------------------------------------------\n"
25795 << "Already assigned degree: ("
25796 << global_node_degree[shd_node_pt] << ")\n"
25797 << "New found degree: (" << node_degree << ")\n"
25798 << "---------------------------------------------------------\n"
25799 << "Node coordinates: (" << shd_node_pt->x(0) << ", "
25800 << shd_node_pt->x(1) << ")\n\n";
25801 throw OomphLibError(
25802 error_stream.str(),
25803 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25804 OOMPH_EXCEPTION_LOCATION);
25805 }
25806
25807 } // else if (!node_done[shd_node_pt])
25808#endif // #ifdef PARANOID
25809
25810 } // for (ishd < n_nodes)
25811
25812 } // if (iproc != my_rank)
25813
25814 } // for (iproc < nproc)
25815
25816#ifdef PARANOID
25817 // Ensure that all the info. sent from root processor has been read
25818 if (decode_counter != n_unsigned_data_received_from_root)
25819 {
25820 std::ostringstream error_stream;
25821 error_stream
25822 << "The number of data decoded received from root processor is\n"
25823 << "different from the total number of data received from the root\n"
25824 << "processor\n"
25825 << "Data decoded: (" << decode_counter << ")\n"
25826 << "Data received: (" << n_unsigned_data_received_from_root << ")\n\n"
25827 << "This is a synchronisation issue so you are probably sending\n"
25828 << "more or less info. than the one that is being decoded\n\n";
25829 throw OomphLibError(
25830 error_stream.str(),
25831 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25832 OOMPH_EXCEPTION_LOCATION);
25833 }
25834#endif
25835 }
25836
25837 //======================================================================
25838 // Sort the nodes on the new shared boundaries (after load balancing),
25839 // computes the alias of the nodes and creates the adjacency matrix
25840 // that represent the graph created by the shared edges between each
25841 // pair of processors
25842 // ======================================================================
25843 template<class ELEMENT>
25846 Vector<Vector<FiniteElement*>>& unsorted_face_ele_pt,
25847 Vector<Vector<Node*>>& tmp_sorted_shared_node_pt,
25848 std::map<Node*, Vector<Vector<unsigned>>>& node_alias,
25849 Vector<Vector<Vector<unsigned>>>& adjacency_matrix)
25850 {
25851 // Get the number of processors and the rank
25852 const unsigned nproc = this->communicator_pt()->nproc();
25853 const unsigned my_rank = this->communicator_pt()->my_rank();
25854
25855 // Assign a unique id to each node shared between each pair of
25856 // processors, in this case the current processor and the iproc
25857
25858 // ... also compute the alias of each node (processor and index of
25859 // the node in all processors where it appears)
25860
25861 // Clear the alias info
25862 node_alias.clear();
25863
25864 // Temporary storage for the index of the nodes
25865 Vector<std::map<Node*, unsigned>> tmp_node_index(nproc);
25866
25867 // Loop over the processors
25868 for (unsigned iproc = 0; iproc < nproc; iproc++)
25869 {
25870 // There is no shared elements between the same processor
25871 if (iproc != my_rank)
25872 {
25873 // Map to mark those nodes already visited
25874 std::map<Node*, bool> done_node;
25875
25876 // A map is used to sort the nodes using their coordinates as
25877 // the key of the map
25878 // std::map<std::pair<double, double>, Node*> sorted_nodes_pt;
25879 std::map<std::pair<double, double>, Node*, classcomp> sorted_nodes_pt;
25880
25881 // Get the number of unsorted face elements
25882 const unsigned n_unsorted_face_ele = unsorted_face_ele_pt[iproc].size();
25883
25884 // Loop over the unsorted elements
25885 for (unsigned e = 0; e < n_unsorted_face_ele; e++)
25886 {
25887 // Get a root element
25888 FiniteElement* face_ele_pt = unsorted_face_ele_pt[iproc][e];
25889 // Get the left node of the face element
25890 Node* left_node_pt = face_ele_pt->node_pt(0);
25891
25892 // Check if the node has been already sorted in the
25893 // interaction between the current processor and iproc
25894 // processor
25895 if (!done_node[left_node_pt])
25896 {
25897 std::pair<double, double> vertex =
25898 std::make_pair(left_node_pt->x(0), left_node_pt->x(1));
25899 sorted_nodes_pt[vertex] = left_node_pt;
25900 // Mark the node as done
25901 done_node[left_node_pt] = true;
25902 }
25903
25904 // Get the number of nodes of the face element
25905 const unsigned n_nodes = face_ele_pt->nnode();
25906 // Get the right node of the face element
25907 Node* right_node_pt = face_ele_pt->node_pt(n_nodes - 1);
25908
25909 // Check if the node has been already sorted in the
25910 // interaction between the current processor and iproc
25911 // processor
25912 if (!done_node[right_node_pt])
25913 {
25914 std::pair<double, double> vertex =
25915 std::make_pair(right_node_pt->x(0), right_node_pt->x(1));
25916 sorted_nodes_pt[vertex] = right_node_pt;
25917 // Mark the node as done
25918 done_node[right_node_pt] = true;
25919 }
25920
25921 } // for (e < nunsorted_face_ele)
25922
25923 // The nodes are already sorted, we need to return them in the
25924 // proper container
25925
25926 // The counter to enumerate the nodes
25927 unsigned counter = 0;
25928
25929 // Go through the map container which already have the nodes
25930 // sorted they have the same sorting on all processors
25931 for (std::map<std::pair<double, double>, Node*>::iterator it =
25932 sorted_nodes_pt.begin();
25933 it != sorted_nodes_pt.end();
25934 it++)
25935 {
25936 // Get the node
25937 Node* node_pt = (*it).second;
25938 // Store the node at the corresponding index
25939 tmp_sorted_shared_node_pt[iproc].push_back(node_pt);
25940
25941 // Create the temporary access to the node index
25942 tmp_node_index[iproc][node_pt] = counter;
25943
25944 // Fill the info. for the node alias
25945 Vector<unsigned> alias(3);
25946 // The current processor
25947 alias[0] = my_rank;
25948 // The processor with which is shared
25949 alias[1] = iproc;
25950 // The index with that processor
25951 alias[2] = counter++;
25952
25953 // Store the info. of the alias
25954 node_alias[node_pt].push_back(alias);
25955
25956 } // Loop map
25957
25958 } // if (iproc != my_rank)
25959
25960 } // for (iproc < nproc)
25961
25962 // Loop over the processors to resize and initialize the adjacency
25963 // matrix
25964 for (unsigned iproc = 0; iproc < nproc; iproc++)
25965 {
25966 // Get the number of nodes shared with iproc
25967 const unsigned n_shd_nodes = tmp_sorted_shared_node_pt[iproc].size();
25968 // Resize the adjacency matrix
25969 adjacency_matrix[iproc].resize(n_shd_nodes);
25970 for (unsigned i = 0; i < n_shd_nodes; i++)
25971 {
25972 // Resize the adjacency matrix
25973 adjacency_matrix[iproc][i].resize(n_shd_nodes);
25974
25975 // Initialize the
25976 for (unsigned j = 0; j < n_shd_nodes; j++)
25977 {
25978 adjacency_matrix[iproc][i][j] = 0;
25979 } // for (j < n_shd_nodes)
25980
25981 } // for (i < n_shd_nodes)
25982
25983 } // for (iproc < nproc)
25984
25985 // Loop over the processors to fill the adjacency matrix
25986 for (unsigned iproc = 0; iproc < nproc; iproc++)
25987 {
25988 // There is no shared elements between the same processor
25989 if (iproc != my_rank)
25990 {
25991 // Get the number of unsorted face elements
25992 const unsigned n_unsorted_face_ele = unsorted_face_ele_pt[iproc].size();
25993
25994 // Loop over the unsorted elements
25995 for (unsigned e = 0; e < n_unsorted_face_ele; e++)
25996 {
25997 // Get a root element
25998 FiniteElement* face_ele_pt = unsorted_face_ele_pt[iproc][e];
25999 // Get the left node of the face element
26000 Node* left_node_pt = face_ele_pt->node_pt(0);
26001
26002 // Get the number of nodes of the face element
26003 const unsigned n_nodes = face_ele_pt->nnode();
26004 // Get the right node of the face element
26005 Node* right_node_pt = face_ele_pt->node_pt(n_nodes - 1);
26006
26007 // Get the index of each of the nodes
26008 const unsigned left_node_index = tmp_node_index[iproc][left_node_pt];
26009 const unsigned right_node_index =
26010 tmp_node_index[iproc][right_node_pt];
26011
26012 // Add an entry to the adjacency matrix to indicate the
26013 // association of left and right node
26014 adjacency_matrix[iproc][left_node_index][right_node_index]++;
26015 // ... both directions
26016 adjacency_matrix[iproc][right_node_index][left_node_index]++;
26017
26018 } // for (e < n_unsorted_face_ele)
26019
26020 } // if (iproc != my_rank)
26021
26022 } // for (iproc < nproc)
26023 }
26024
26025 //======================================================================
26026 /// Get the nodes on the shared boundary (b), these are stored
26027 /// in the segment they belong
26028 //======================================================================
26029 template<class ELEMENT>
26032 const unsigned& shd_bnd_id, Vector<Vector<Node*>>& tmp_segment_nodes)
26033 {
26034 // Clear the data structure were to return the nodes
26035 tmp_segment_nodes.clear();
26036
26037 // Get the face elements that created the shared boundary from the
26038 // bulk shared boundary elements
26039
26040#ifdef PARANOID
26041 // The temporary storage for the halo face elements
26042 Vector<FiniteElement*> halo_shared_face_ele_pt;
26043#endif
26044 // The temporary storage for the nonhalo face elements
26045 Vector<FiniteElement*> nonhalo_shared_face_ele_pt;
26046
26047 // Get the number of shared boundary elements associated with the
26048 // current shared boundary
26049 const unsigned nshared_bound_ele =
26050 this->nshared_boundary_element(shd_bnd_id);
26051
26052 // Loop over the elements in the shared boundary to create the face
26053 // elements
26054 for (unsigned e = 0; e < nshared_bound_ele; e++)
26055 {
26056 // Get the shared boundary element
26057 FiniteElement* bulk_ele_pt =
26058 this->shared_boundary_element_pt(shd_bnd_id, e);
26059
26060 // Get the face index
26061 int face_index = this->face_index_at_shared_boundary(shd_bnd_id, e);
26062
26063 // Before adding the new element we need to ensure that the edge
26064 // that this element represents has not been already added
26065 FiniteElement* face_ele_pt =
26066 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
26067
26068 // Nonhalo element
26069 if (!bulk_ele_pt->is_halo())
26070 {
26071 // Add nonhalo shared face element to the container
26072 nonhalo_shared_face_ele_pt.push_back(face_ele_pt);
26073 }
26074#ifdef PARANOID
26075 else // halo element
26076 {
26077 // Add halo shared face element to the container
26078 halo_shared_face_ele_pt.push_back(face_ele_pt);
26079 }
26080#endif
26081
26082 } // for (e < nshared_bound_ele)
26083
26084 // Mark the face elements already used
26085 std::map<FiniteElement*, bool> shared_face_done;
26086
26087 // Get the number of nonhalo face elements
26088 const unsigned nnonhalo_face_shared_ele = nonhalo_shared_face_ele_pt.size();
26089
26090 // If we are in PARANOID mode check that there is one halo element
26091 // for each nonhalo element
26092#ifdef PARANOID
26093 // Get the number of halo face elements
26094 const unsigned nhalo_face_shared_ele = halo_shared_face_ele_pt.size();
26095
26096 // The number of nonhalo shared face boundary elements must be the
26097 // half of the total number of shared boundary elements
26098 if (nshared_bound_ele / 2 != nnonhalo_face_shared_ele)
26099 {
26100 std::ostringstream error_message;
26101 error_message
26102 << "The number of shared boundary elements (" << nshared_bound_ele
26103 << ") is not the double\nof the number of unsorted nonhalo shared "
26104 << "face boundary elements (" << nnonhalo_face_shared_ele
26105 << ")\n for the current boundary (" << shd_bnd_id << ")\n\n";
26106 throw OomphLibError(
26107 error_message.str(),
26108 "RefineableTriangleMesh::get_shared_boundary_segment_nodes_helper()",
26109 OOMPH_EXCEPTION_LOCATION);
26110 }
26111
26112 // The number of halo shared face boundary elements must be the
26113 // half of the total number of shared boundary elements
26114 if (nshared_bound_ele / 2 != nhalo_face_shared_ele)
26115 {
26116 std::ostringstream error_message;
26117 error_message
26118 << "The number of shared boundary elements (" << nshared_bound_ele
26119 << ") is not the double\nof the number of unsorted halo shared "
26120 << "face boundary elements (" << nhalo_face_shared_ele
26121 << ")\n for the current boundary (" << shd_bnd_id << ")\n\n";
26122 throw OomphLibError(
26123 error_message.str(),
26124 "RefineableTriangleMesh::get_shared_boundary_segment_nodes_helper()",
26125 OOMPH_EXCEPTION_LOCATION);
26126 }
26127
26128 // ------------------------------------------------------------------
26129 // Loop over the nonhalo face elements and look for the halo face
26130 // element at the other side of the shared boundary
26131 for (unsigned inh = 0; inh < nnonhalo_face_shared_ele; inh++)
26132 {
26133 // Get the inh-th face element
26134 FiniteElement* nonhalo_face_ele_pt = nonhalo_shared_face_ele_pt[inh];
26135
26136 // Get the number of nodes on the face element
26137 const unsigned nnodes_nh = nonhalo_face_ele_pt->nnode();
26138 // Get the first and last node on the element
26139 Node* nh_first_node_pt = nonhalo_face_ele_pt->node_pt(0);
26140 Node* nh_last_node_pt = nonhalo_face_ele_pt->node_pt(nnodes_nh - 1);
26141
26142 // Now find the (halo) face element at the other side of the
26143 // shared boundary
26144 for (unsigned ih = 0; ih < nhalo_face_shared_ele; ih++)
26145 {
26146 // Get the ih-th face element
26147 FiniteElement* halo_face_ele_pt = halo_shared_face_ele_pt[ih];
26148
26149 // Check that the face element has not been done
26150 if (!shared_face_done[halo_face_ele_pt])
26151 {
26152 // Get the number of nodes on the face element
26153 const unsigned nnodes_h = halo_face_ele_pt->nnode();
26154 // Get the first and last node on the element
26155 Node* h_first_node_pt = halo_face_ele_pt->node_pt(0);
26156 Node* h_last_node_pt = halo_face_ele_pt->node_pt(nnodes_h - 1);
26157
26158 // If the nodes are the same then we have found the (halo)
26159 // face element at the other side of the shared boundary
26160 if (nh_first_node_pt == h_first_node_pt &&
26161 nh_last_node_pt == h_last_node_pt)
26162 {
26163 // Mark the face elements as done
26164 shared_face_done[nonhalo_face_ele_pt] = true;
26165 shared_face_done[halo_face_ele_pt] = true;
26166
26167 // Break the loop for (ih < nhalo_face_shared_ele)
26168 break;
26169 } // if (nh_first_node_pt == h_first_node_pt &&
26170 // nh_last_node_pt == h_last_node_pt)
26171 else if (nh_first_node_pt == h_last_node_pt &&
26172 nh_last_node_pt == h_first_node_pt)
26173 {
26174 // Mark the face elements as done
26175 shared_face_done[nonhalo_face_ele_pt] = true;
26176 shared_face_done[halo_face_ele_pt] = true;
26177
26178 // Break the loop for (ih < nhalo_face_shared_ele)
26179 break;
26180 } // else if (nh_first_node_pt == h_last_node_pt &&
26181 // nh_last_node_pt == h_first_node_pt)
26182
26183 } // if (face_done[halo_face_ele_pt])
26184
26185 } // for (ih < nhalo_face_shared_ele)
26186
26187 } // for (inh < nnonhalo_face_shared_ele)
26188
26189 // The number of done shared face elements MUST be the same as the
26190 // sum of the nonhalo and halo shared boundary face elements
26191 if ((nnonhalo_face_shared_ele + nhalo_face_shared_ele) !=
26192 shared_face_done.size())
26193 {
26194 std::ostringstream error_message;
26195 error_message << "The number of DONE shared boundary face elements ("
26196 << shared_face_done.size()
26197 << ") is not the same\n as the sum of"
26198 << "the nonhalo face shared boundary elements ("
26199 << nnonhalo_face_shared_ele
26200 << ")\nand the halo face shared "
26201 << "boundary elements (" << nhalo_face_shared_ele
26202 << ") for the\n/"
26203 << "current boundary (" << shd_bnd_id << ")\n\n";
26204 throw OomphLibError(
26205 error_message.str(),
26206 "RefineableTriangleMesh::get_shared_boundary_segment_nodes_helper()",
26207 OOMPH_EXCEPTION_LOCATION);
26208 }
26209#endif // #ifdef PARANOID
26210
26211 // -------------------------------------------------------------
26212 // Now sort the face elements
26213 // -------------------------------------------------------------
26214
26215 // We already have the shared face elements that make the shared
26216 // boundary now sort them to create a contiguous boundary
26217
26218 // Clear the already done face elements
26219 shared_face_done.clear();
26220
26221 unsigned nsorted_face_ele = 0;
26222
26223 // Storing for the sorting nodes extracted from the face elements
26224 std::list<Node*> sorted_nodes;
26225
26226 // Get the root face element
26227 FiniteElement* root_face_ele_pt = nonhalo_shared_face_ele_pt[0];
26228 nsorted_face_ele++;
26229
26230 // Mark face as done
26231 shared_face_done[root_face_ele_pt] = true;
26232
26233 // The initial and final node on the list
26234 const unsigned nnodes_root = root_face_ele_pt->nnode();
26235 Node* first_node_pt = root_face_ele_pt->node_pt(0);
26236 Node* last_node_pt = root_face_ele_pt->node_pt(nnodes_root - 1);
26237
26238 // Push back on the list the new nodes
26239 sorted_nodes.push_back(first_node_pt);
26240 sorted_nodes.push_back(last_node_pt);
26241
26242 // Sort the face elements
26243 while (nsorted_face_ele < nnonhalo_face_shared_ele)
26244 {
26245 // Flag to indicate when a node was added
26246 bool node_added = false;
26247
26248 // Start from the next edge since we have already added the
26249 // previous one as the initial face element
26250 for (unsigned iface = 1; iface < nnonhalo_face_shared_ele; iface++)
26251 {
26252 FiniteElement* tmp_shared_face_ele_pt =
26253 nonhalo_shared_face_ele_pt[iface];
26254
26255 // If face has not been sorted
26256 if (!shared_face_done[tmp_shared_face_ele_pt])
26257 {
26258 // Get the number of nodes for the current face element
26259 const unsigned tmp_nnodes = tmp_shared_face_ele_pt->nnode();
26260
26261 // Get each individual node
26262 Node* left_node_pt = tmp_shared_face_ele_pt->node_pt(0);
26263 Node* right_node_pt = tmp_shared_face_ele_pt->node_pt(tmp_nnodes - 1);
26264
26265 if (left_node_pt == first_node_pt)
26266 {
26267 // Push front the new node
26268 sorted_nodes.push_front(right_node_pt);
26269 first_node_pt = right_node_pt;
26270 node_added = true;
26271 }
26272 else if (left_node_pt == last_node_pt)
26273 {
26274 // Push back the new node
26275 sorted_nodes.push_back(right_node_pt);
26276 last_node_pt = right_node_pt;
26277 node_added = true;
26278 }
26279 else if (right_node_pt == first_node_pt)
26280 {
26281 // Push front the new node
26282 sorted_nodes.push_front(left_node_pt);
26283 first_node_pt = left_node_pt;
26284 node_added = true;
26285 }
26286 else if (right_node_pt == last_node_pt)
26287 {
26288 // Push back the new node
26289 sorted_nodes.push_back(left_node_pt);
26290 last_node_pt = left_node_pt;
26291 node_added = true;
26292 }
26293
26294 if (node_added)
26295 {
26296 // Mark as done only if one of its nodes has been added to
26297 // the list
26298 shared_face_done[tmp_shared_face_ele_pt] = true;
26299 nsorted_face_ele++;
26300
26301 // Break the for
26302 break;
26303 }
26304
26305 } // if (!shared_face_done[tmp_shared_face_ele_pt])
26306
26307 } // for (iface < nnonhalo_face_shared_ele)
26308
26309 } // while (nsorted_face_ele < nnonhalo_face_shared_ele))
26310
26311 // Here we can safely delete the face elements, they are no longer
26312 // required
26313
26314 // First the nonhalo face elements
26315 for (unsigned inh = 0; inh < nnonhalo_face_shared_ele; inh++)
26316 {
26317 delete nonhalo_shared_face_ele_pt[inh];
26318 nonhalo_shared_face_ele_pt[inh] = 0;
26319 } // for (inh < nnonhalo_face_shared_ele)
26320
26321#ifdef PARANOID
26322 // ... then the halo face elements
26323 for (unsigned ih = 0; ih < nhalo_face_shared_ele; ih++)
26324 {
26325 delete halo_shared_face_ele_pt[ih];
26326 halo_shared_face_ele_pt[ih] = 0;
26327 } // for (inh < nhalo_face_shared_ele)
26328#endif
26329
26330 // ------------------------------------------------
26331 // Now copy the nodes to the output container
26332 // ------------------------------------------------
26333 // Get the number of nodes in the container
26334 const unsigned n_nodes = sorted_nodes.size();
26335
26336 // First resize the container
26337 tmp_segment_nodes.resize(1);
26338 tmp_segment_nodes[0].resize(n_nodes);
26339
26340 // Counter
26341 unsigned counter = 0;
26342
26343 // Loop over the list of nodes and copy them in the output container
26344 for (std::list<Node*>::iterator it = sorted_nodes.begin();
26345 it != sorted_nodes.end();
26346 it++)
26347 {
26348 tmp_segment_nodes[0][counter] = (*it);
26349 counter++;
26350 } // Loop over sorted nodes
26351 }
26352
26353 //=====start of get_required_elemental_information_load_balance_helper====
26354 /// Helper function to get the required elemental information from
26355 /// the element that will be sent to iproc processor.
26356 /// This info. involves the association of the element to a boundary or
26357 /// region.
26358 //========================================================================
26359 template<class ELEMENT>
26362 unsigned& iproc,
26363 Vector<Vector<FiniteElement*>>& f_haloed_ele_pt,
26364 FiniteElement* ele_pt)
26365 {
26366 // Check if the element is associated with the original boundaries
26367 const unsigned nbound = this->initial_shared_boundary_id();
26368
26369 // Get the number of processors
26370 const unsigned nproc = this->communicator_pt()->nproc();
26371
26372 // ------------------------------------------------------------------
26373 // Stores the information regarding the boundaries associated to the
26374 // element (it that is the case)
26375 Vector<unsigned> associated_boundaries;
26376 Vector<unsigned> face_index_on_boundary;
26377
26378 unsigned counter_face_indexes = 0;
26379
26380 for (unsigned b = 0; b < nbound; b++)
26381 {
26382 // Get the number of elements associated to boundary i
26383 const unsigned nboundary_ele = nboundary_element(b);
26384 for (unsigned e = 0; e < nboundary_ele; e++)
26385 {
26386 if (ele_pt == this->boundary_element_pt(b, e))
26387 {
26388 // Keep track of the boundaries associated to the element
26389 associated_boundaries.push_back(b);
26390 // Get the face index
26391 face_index_on_boundary.push_back(face_index_at_boundary(b, e));
26392 counter_face_indexes++;
26393#ifdef PARANOID
26394 if (counter_face_indexes > 2)
26395 {
26396 std::stringstream error_message;
26397 error_message
26398 << "A triangular element can not have more than two of its faces "
26399 << "on a boundary!!!\n\n";
26400 throw OomphLibError(error_message.str(),
26401 "RefineableTriangleMesh::get_required_"
26402 "elemental_information_helper()",
26403 OOMPH_EXCEPTION_LOCATION);
26404 }
26405#else
26406 // Already found 2 face indexes on the same boundary?
26407 if (counter_face_indexes == 2)
26408 {
26409 break;
26410 }
26411#endif // #ifdef PARANOID
26412
26413 } // if (ele_pt == this->boundary_element_pt(b,e))
26414
26415 } // (e < nboundary_ele)
26416
26417 } // (b < nbound)
26418
26419 // If the element is associated to any boundary then package all the
26420 // relevant info
26421 const unsigned nassociated_boundaries = associated_boundaries.size();
26422 if (nassociated_boundaries > 0)
26423 {
26424 Flat_packed_unsigneds.push_back(1);
26425#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26427 "The element is a boundary element");
26428#endif
26429 Flat_packed_unsigneds.push_back(nassociated_boundaries);
26430#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26431 std::stringstream junk;
26432 junk << "The elements is associated to " << nassociated_boundaries
26433 << " boundaries";
26434 Flat_packed_unsigneds_string.push_back(junk.str());
26435#endif
26436
26437 // Package the ids of the associated boundaries and the
26438 // corresponding face index for each boundary (if the element is a
26439 // corner element, it will have two faces associated to the
26440 // boundary)
26441 for (unsigned i = 0; i < nassociated_boundaries; i++)
26442 {
26443 unsigned b = associated_boundaries[i];
26444 Flat_packed_unsigneds.push_back(b);
26445#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26446 std::stringstream junk;
26447 junk << "Element associated to boundary " << b << " of "
26448 << nassociated_boundaries << " total associated boundaries";
26449 Flat_packed_unsigneds_string.push_back(junk.str());
26450#endif
26451 unsigned f = face_index_on_boundary[i];
26452 Flat_packed_unsigneds.push_back(f);
26453#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26454 std::stringstream junk2;
26455 junk2 << "Face index " << f << " for associated boundary " << b;
26456 Flat_packed_unsigneds_string.push_back(junk2.str());
26457#endif
26458 }
26459
26460 // If the element is associated to any boundary then we should
26461 // check if the mesh has regions, if that is the case then we need
26462 // to check to which region the boundary element does belong
26463
26464 // If the mesh has regions we should look for the element
26465 // associated to a boundary and a specified region
26466 Vector<Vector<unsigned>> associated_boundaries_and_regions;
26467 Vector<unsigned> face_index_on_boundary_and_region;
26468
26469 // Now check for the case when we have regions in the mesh
26470 const unsigned n_regions = this->nregion();
26471 if (n_regions > 1)
26472 {
26473 // Used to count the number of faces associated with
26474 // boundary-regions
26475 unsigned counter_face_indexes_in_regions = 0;
26476 // Loop over the boundaries
26477 for (unsigned b = 0; b < nbound; b++)
26478 {
26479 // Go through each region by getting the region id
26480 for (unsigned i_reg = 0; i_reg < n_regions; i_reg++)
26481 {
26482 // Get thre region id associated with the (i_reg)-th region
26483 const unsigned region_id =
26484 static_cast<unsigned>(this->Region_attribute[i_reg]);
26485
26486 // Loop over all elements associated with the current boundary
26487 // and the i_reg-th region and check if the element is part of
26488 // any region
26489 const unsigned nele_in_region =
26490 this->nboundary_element_in_region(b, region_id);
26491 for (unsigned ee = 0; ee < nele_in_region; ee++)
26492 {
26493 // Check if the boundary-region element is the same as the
26494 // element
26495 if (ele_pt ==
26496 this->boundary_element_in_region_pt(b, region_id, ee))
26497 {
26498 // Storage for the boundary and region associated to the
26499 // element
26500 Vector<unsigned> bound_and_region(2);
26501
26502 // Keep track of the boundaries associated to the element
26503 bound_and_region[0] = b;
26504 // Keep track of the regions associated to the element
26505 bound_and_region[1] = region_id;
26506 // Add the boundaries and regions in the storage to be
26507 // sent to other processors
26508 associated_boundaries_and_regions.push_back(bound_and_region);
26509 // Get the face index and keep track of it
26510 face_index_on_boundary_and_region.push_back(
26511 this->face_index_at_boundary_in_region(b, region_id, ee));
26512
26513 // Increase the number of faces of the element associated
26514 // to boundary-regions
26515 counter_face_indexes_in_regions++;
26516
26517#ifdef PARANOID
26518 if (counter_face_indexes_in_regions > 2)
26519 {
26520 std::stringstream error_message;
26521 error_message << "A triangular element can not have more "
26522 "than two of its\n"
26523 << "faces on a boundary!!!\n\n";
26524 throw OomphLibError(error_message.str(),
26525 "RefineableTriangleMesh::get_required_"
26526 "elemental_information_helper()",
26527 OOMPH_EXCEPTION_LOCATION);
26528 } // if (counter_face_indexes_in_regions > 2)
26529#endif
26530
26531 } // The element is a boundary-region element
26532
26533 } // for (ee < nele_in_region)
26534
26535 } // for (i_reg < n_regions)
26536
26537 } // for (b < nbound)
26538
26539 } // if (n_regions > 1)
26540
26541 // Now package the info. to be sent to other processors
26542 const unsigned nassociated_boundaries_and_regions =
26543 associated_boundaries_and_regions.size();
26544 if (nassociated_boundaries_and_regions > 0)
26545 {
26546 Flat_packed_unsigneds.push_back(1);
26547#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26549 "The element is associated to boundaries and regions");
26550#endif
26551
26552 Flat_packed_unsigneds.push_back(nassociated_boundaries_and_regions);
26553#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26554 std::stringstream junk;
26555 junk << "The element is associated to "
26556 << nassociated_boundaries_and_regions << " boundaries-regions";
26557 Flat_packed_unsigneds_string.push_back(junk.str());
26558#endif
26559
26560 // Package the ids of the associated boundaries, regions and the
26561 // corresponding face index for each boundary-region (if the
26562 // element is a corner element, it will have two faces
26563 // associated to the boundary-region)
26564 for (unsigned i = 0; i < nassociated_boundaries_and_regions; i++)
26565 {
26566 const unsigned b = associated_boundaries_and_regions[i][0];
26567 Flat_packed_unsigneds.push_back(b);
26568#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26569 std::stringstream junk;
26570 junk << "Element associated to boundary " << b << " of "
26571 << nassociated_boundaries_and_regions
26572 << " total associated boundaries-regions";
26573 Flat_packed_unsigneds_string.push_back(junk.str());
26574#endif
26575
26576 const unsigned r = associated_boundaries_and_regions[i][1];
26577 Flat_packed_unsigneds.push_back(r);
26578#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26579 std::stringstream junk2;
26580 junk2 << "Element associated to region " << r << " of "
26581 << nassociated_boundaries_and_regions
26582 << " total associated boundaries-regions";
26583 Flat_packed_unsigneds_string.push_back(junk2.str());
26584#endif
26585
26586 const unsigned f = face_index_on_boundary_and_region[i];
26587 Flat_packed_unsigneds.push_back(f);
26588#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26589 std::stringstream junk3;
26590 junk3 << "Face index " << f << " for associated boundary-region ("
26591 << b << "-" << r << ")";
26592 Flat_packed_unsigneds_string.push_back(junk3.str());
26593#endif
26594 } // for (i < nassociated_boundaries_and_regions)
26595 } // if (nassociated_boundaries_and_regions > 0)
26596 else
26597 {
26598 Flat_packed_unsigneds.push_back(0);
26599#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26601 "The element is NOT associated to boundaries and regions");
26602#endif
26603 } // else if (nassociated_boundaries_and_regions > 0)
26604 }
26605 else
26606 {
26607 Flat_packed_unsigneds.push_back(0);
26608#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26610 "The element is not associated to any original boundary");
26611#endif
26612 }
26613
26614 // ------------------------------------------------------------
26615 // Now review if the element is associated to a shared boundary
26616
26617 // Store the shared boundaries, and therefore the face indexes
26618 // associated to the element
26619 Vector<unsigned> associated_shared_boundaries;
26620 Vector<unsigned> face_index_on_shared_boundary;
26621
26622 // Get the shared boundaries in this processor
26623 Vector<unsigned> my_rank_shared_boundaries_ids;
26624 this->shared_boundaries_in_this_processor(my_rank_shared_boundaries_ids);
26625
26626 // Get the number of shared boundaries
26627 const unsigned nmy_rank_shd_bnd = my_rank_shared_boundaries_ids.size();
26628 // Loop over the shared boundaries
26629 for (unsigned i = 0; i < nmy_rank_shd_bnd; i++)
26630 {
26631 // Get the boundary id
26632 const unsigned sb = my_rank_shared_boundaries_ids[i];
26633
26634 // Get the number of elements associated to shared boundary sb
26635 const unsigned nboundary_ele = this->nshared_boundary_element(sb);
26636 for (unsigned e = 0; e < nboundary_ele; e++)
26637 {
26638 if (ele_pt == this->shared_boundary_element_pt(sb, e))
26639 {
26640 // Keep track of the boundaries associated to the element
26641 associated_shared_boundaries.push_back(sb);
26642 // Get the face index
26643 face_index_on_shared_boundary.push_back(
26644 this->face_index_at_shared_boundary(sb, e));
26645 }
26646 } // (e < nboundary_ele)
26647 } // (i < nmy_rank_shd_bnd)
26648
26649 // If the element is associated to a shared boundary then package
26650 // all the relevant info
26651 const unsigned nassociated_shared_boundaries =
26652 associated_shared_boundaries.size();
26653 if (nassociated_shared_boundaries > 0)
26654 {
26655 Flat_packed_unsigneds.push_back(3);
26656#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26658 "The element is a shared boundary element");
26659#endif
26660 Flat_packed_unsigneds.push_back(nassociated_shared_boundaries);
26661#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26662 std::stringstream junk;
26663 junk << "The elements is associated to " << nassociated_shared_boundaries
26664 << "shared boundaries";
26665 Flat_packed_unsigneds_string.push_back(junk.str());
26666#endif
26667
26668 // Package the ids of the associated boundaries
26669 for (unsigned i = 0; i < nassociated_shared_boundaries; i++)
26670 {
26671 const unsigned b = associated_shared_boundaries[i];
26672 Flat_packed_unsigneds.push_back(b);
26673#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26674 std::stringstream junk;
26675 junk << "Element associated to shared boundary " << b << " of "
26676 << nassociated_shared_boundaries << " total associated boundaries";
26677 Flat_packed_unsigneds_string.push_back(junk.str());
26678#endif
26679
26680 const unsigned f = face_index_on_shared_boundary[i];
26681 Flat_packed_unsigneds.push_back(f);
26682#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26683 std::stringstream junk2;
26684 junk2 << "Face index " << f << " for associated shared boundary " << b;
26685 Flat_packed_unsigneds_string.push_back(junk2.str());
26686#endif
26687 }
26688 }
26689 else
26690 {
26691 Flat_packed_unsigneds.push_back(0);
26692#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26694 "The element is not associated to any shared boundary");
26695#endif
26696 }
26697
26698 // Now check if the element is haloed with any processor
26699
26700 // Store the index of the haloed element with the jproc
26701 Vector<Vector<unsigned>> index_haloed(nproc);
26702
26703 // Loop over the processors
26704 for (unsigned jproc = 0; jproc < nproc; jproc++)
26705 {
26706 // Get the number of haloed elements with jproc
26707 const unsigned n_haloed_jproc = f_haloed_ele_pt[jproc].size();
26708 // Loop over the haloed elements with jproc
26709 for (unsigned ihd = 0; ihd < n_haloed_jproc; ihd++)
26710 {
26711 // Is a haloed element?
26712 if (ele_pt == f_haloed_ele_pt[jproc][ihd])
26713 {
26714 // Store the haloed index with the jproc processor
26715 index_haloed[jproc].push_back(ihd);
26716 // Break the searching with the jproc processor
26717 break;
26718 } // if (ele_pt == f_haloed_ele_pt[jproc][ihd])
26719
26720 } // for (ihd < n_haloed_jproc)
26721
26722 } // for (jproc < nproc)
26723
26724 // Send the haloed info.
26725 // Loop over the processors
26726 for (unsigned jproc = 0; jproc < nproc; jproc++)
26727 {
26728 // Is the element haloed with the jproc processor
26729 const unsigned n_index_haloed_jproc = index_haloed[jproc].size();
26730 Flat_packed_unsigneds.push_back(n_index_haloed_jproc);
26731#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26733 "The number of haloed indexes the element is with processor jproc");
26734#endif
26735 for (unsigned ihd = 0; ihd < n_index_haloed_jproc; ihd++)
26736 {
26737 Flat_packed_unsigneds.push_back(index_haloed[jproc][ihd]);
26738#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26740 "The haloed index of the element with jproc");
26741#endif
26742 } // for (ihd < n_index_haloed_jproc)
26743
26744 } // for (jproc < nproc)
26745 }
26746
26747 //======================================================================
26748 /// Helper function to add nodes on a new domain as a result of
26749 /// load balance
26750 //======================================================================
26751 template<class ELEMENT>
26753 unsigned& iproc,
26754 Vector<Vector<FiniteElement*>>& f_halo_ele_pt,
26755 Vector<Node*>& new_nodes_on_domain,
26756 Node* nod_pt)
26757 {
26758 // Attempt to add this node to the new domain
26759 const unsigned nnew_nodes_on_domain = new_nodes_on_domain.size();
26760 const unsigned new_added_node_index =
26761 this->try_to_add_node_pt_load_balance(new_nodes_on_domain, nod_pt);
26762
26763 // If it was added then the new index should match the size of the storage
26764 if (new_added_node_index == nnew_nodes_on_domain)
26765 {
26766 Flat_packed_unsigneds.push_back(1);
26767
26768#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26769 std::stringstream junk;
26770 junk << "Node needs to be constructed [size="
26771 << Flat_packed_unsigneds.size() << "]; last entry: "
26773 Flat_packed_unsigneds_string.push_back(junk.str());
26774#endif
26775
26776 // This helper function gets all the required information for the
26777 // specified node and stores it into MPI-sendable information
26778 // so that a new copy can be made on the receiving process
26779 get_required_nodal_information_load_balance_helper(
26780 f_halo_ele_pt, iproc, nod_pt);
26781 }
26782 else // It was already added
26783 {
26784 Flat_packed_unsigneds.push_back(0);
26785#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26786 std::stringstream junk;
26787 junk << "Node was already added [size=" << Flat_packed_unsigneds.size()
26788 << "]; last entry: "
26790
26791 Flat_packed_unsigneds_string.push_back(junk.str());
26792#endif
26793
26794 // This node has been already added, so tell the other process
26795 // its index in the equivalent storage
26796 Flat_packed_unsigneds.push_back(new_added_node_index);
26797#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26798 Flat_packed_unsigneds_string.push_back("new added node index");
26799#endif
26800 }
26801 }
26802
26803 //======start of get_required_nodal_information_load_balance_helper=======
26804 /// Helper function to get the required nodal information from an
26805 /// haloed node so that a fully-functional halo node (and therefore element)
26806 /// can be created on the receiving process
26807 //========================================================================
26808 template<class ELEMENT>
26811 Vector<Vector<FiniteElement*>>& f_halo_ele_pt,
26812 unsigned& iproc,
26813 Node* nod_pt)
26814 {
26815 unsigned my_rank = this->communicator_pt()->my_rank();
26816 const unsigned nproc = this->communicator_pt()->nproc();
26817
26818 // Tell the halo copy of this node how many values there are
26819 // [NB this may be different for nodes within the same element, e.g.
26820 // when using Lagrange multipliers]
26821 unsigned n_val = nod_pt->nvalue();
26822 Flat_packed_unsigneds.push_back(n_val);
26823#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26824 Flat_packed_unsigneds_string.push_back("Number of values");
26825#endif
26826
26827 unsigned n_dim = nod_pt->ndim();
26828
26829 // Default number of previous values to 1
26830 unsigned n_prev = 1;
26831 if (this->Time_stepper_pt != 0)
26832 {
26833 // Add number of history values to n_prev
26834 n_prev = this->Time_stepper_pt->ntstorage();
26835 }
26836
26837 // -----------------------------------------------------
26838 // Is the node on an original boundary?
26839 // Store the original boundaries where the node may be
26840 Vector<unsigned> original_boundaries;
26841 // Loop over the original boundaries of the mesh and check if live
26842 // on one of them
26843 const unsigned n_bnd = this->initial_shared_boundary_id();
26844 for (unsigned bb = 0; bb < n_bnd; bb++)
26845 {
26846 // Which boundaries (could be more than one) is it on?
26847 if (nod_pt->is_on_boundary(bb))
26848 {
26849 original_boundaries.push_back(bb);
26850 }
26851 }
26852
26853 const unsigned n_original_boundaries = original_boundaries.size();
26854 // Is the node on any original boundary?
26855 if (n_original_boundaries > 0)
26856 {
26857 // Indicate that the node is on an original boundary
26858 Flat_packed_unsigneds.push_back(2);
26859#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26861 "Node is on the original boundaries");
26862#endif
26863
26864 Flat_packed_unsigneds.push_back(n_original_boundaries);
26865#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26866 std::stringstream junk;
26867 junk << "Node is on " << n_original_boundaries << " original boundaries";
26868 Flat_packed_unsigneds_string.push_back(junk.str());
26869#endif
26870
26871 // Loop over the original boundaries the node is on
26872 for (unsigned i = 0; i < n_original_boundaries; i++)
26873 {
26874 Flat_packed_unsigneds.push_back(original_boundaries[i]);
26875#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26876 std::stringstream junk;
26877 junk << "Node is on boundary " << original_boundaries[i] << " of "
26878 << nb;
26879 Flat_packed_unsigneds_string.push_back(junk.str());
26880#endif
26881 // Get the boundary coordinate of the node
26882 Vector<double> zeta(1);
26883 nod_pt->get_coordinates_on_boundary(original_boundaries[i], zeta);
26884 Flat_packed_doubles.push_back(zeta[0]);
26885 }
26886 }
26887 else
26888 {
26889 // Indicate that the node is NOT on an original boundary
26890 Flat_packed_unsigneds.push_back(0);
26891#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26893 "Node is on any original boundary");
26894#endif
26895 }
26896
26897 // -------------------------------------------------------
26898 // Is the node on shared boundaries?
26899 bool node_on_shared_boundary = false;
26900 // Loop over the shared boundaries with the iproc processors and
26901 // check if live on one of them
26902 const unsigned n_shd_bnd = this->nshared_boundaries(my_rank, iproc);
26903 for (unsigned bb = 0; bb < n_shd_bnd; bb++)
26904 {
26905 // Get the boundary id
26906 unsigned i_bnd = this->shared_boundaries_ids(my_rank, iproc, bb);
26907 // Which boundaries (could be more than one) is it on?
26908 if (this->is_node_on_shared_boundary(i_bnd, nod_pt))
26909 {
26910 node_on_shared_boundary = true;
26911 break;
26912 }
26913 }
26914
26915 // If the node live on any of the shared boundaries with the iproc
26916 // processor then just get the node number according to the
26917 // sorted_shared_boundary_node_pt() scheme and send it accross
26918 if (node_on_shared_boundary)
26919 {
26920 Flat_packed_unsigneds.push_back(1);
26921#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26922 Flat_packed_unsigneds_string.push_back("Node is on shared boundary");
26923#endif
26924
26925 // Store the shared boundaries where the node is on
26926 Vector<unsigned> shd_boundaries;
26927 // Loop over the shared boundaries with the iproc processor
26928 for (unsigned bb = 0; bb < n_shd_bnd; bb++)
26929 {
26930 // Get the boundary id
26931 const unsigned i_bnd = this->shared_boundaries_ids(my_rank, iproc, bb);
26932 // Which boundaries (could be more than one) is it on?
26933 if (this->is_node_on_shared_boundary(i_bnd, nod_pt))
26934 {
26935 shd_boundaries.push_back(i_bnd);
26936 }
26937 }
26938
26939 // Get the number of shared boundaries the node is on
26940 const unsigned n_shd_bnd_is_on = shd_boundaries.size();
26941 // Send the number of shared boundaries the node is on
26942 Flat_packed_unsigneds.push_back(n_shd_bnd_is_on);
26943#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26944 std::stringstream junk;
26945 junk << "Node is on " << n_shd_bnd_is_on << " shared boundaries";
26946 Flat_packed_unsigneds_string.push_back(junk.str());
26947#endif
26948
26949 // Loop over the shared boundaries to send their ids
26950 for (unsigned i = 0; i < n_shd_bnd_is_on; i++)
26951 {
26952 Flat_packed_unsigneds.push_back(shd_boundaries[i]);
26953#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26954 std::stringstream junk;
26955 junk << "Node is on boundary " << shd_boundaries[i] << " of " << nb;
26956 Flat_packed_unsigneds_string.push_back(junk.str());
26957#endif
26958 }
26959
26960 // Given that the node is on at least one boundary get the index
26961 // of the node in one of the boundaries and send this index
26962 unsigned shared_boundary_id = shd_boundaries[0];
26963 // Get the number of nodes on the given shared boundary
26964 const unsigned n_nodes_on_shared_boundary =
26965 nsorted_shared_boundary_node(shared_boundary_id);
26966 // Store the index of the node on the shared boundary
26967 unsigned index_node_on_shared_boundary;
26968#ifdef PARANOID
26969 // Flag to know if the node has been found
26970 bool found_index_node_on_shared_boundary = false;
26971#endif
26972 // Loop over the nodes on the shared boundary to find the node
26973 for (unsigned i = 0; i < n_nodes_on_shared_boundary; i++)
26974 {
26975 // Get the i-th node on the shared boundary
26976 Node* shared_node_pt =
26977 sorted_shared_boundary_node_pt(shared_boundary_id, i);
26978 // Is the node we are looking for
26979 if (shared_node_pt == nod_pt)
26980 {
26981 // Store the index
26982 index_node_on_shared_boundary = i;
26983#ifdef PARANOID
26984 // Mark as found
26985 found_index_node_on_shared_boundary = true;
26986#endif
26987 break; // break
26988 }
26989
26990 } // for (i < nnodes_on_shared_boundary)
26991
26992#ifdef PARANOID
26993 if (!found_index_node_on_shared_boundary)
26994 {
26995 std::ostringstream error_message;
26996 error_message << "The index of the node on boundary ("
26997 << shared_boundary_id << ") was not found.\n"
26998 << "The node coordinates are (" << nod_pt->x(0) << ","
26999 << nod_pt->x(1) << ").\n";
27000 throw OomphLibError(
27001 error_message.str(),
27002 "RefineableTriangleMesh::get_required_nodal_information_helper()",
27003 OOMPH_EXCEPTION_LOCATION);
27004 }
27005#endif
27006 // Send the index of the node on the shared boundary
27007 Flat_packed_unsigneds.push_back(index_node_on_shared_boundary);
27008#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27009 std::stringstream junk2;
27010 junk2 << "Node index on boundary " << boundaries[0] << " is "
27011 << index_node_on_shared_boundary;
27012 Flat_packed_unsigneds_string.push_back(junk2.str());
27013#endif
27014
27015 } // if (node_on_shared_boundary)
27016 else
27017 {
27018 // The node is not on a shared boundary
27019 Flat_packed_unsigneds.push_back(0);
27020#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27022 "Node is not on a shared boundary");
27023#endif
27024 }
27025
27026 // ----------------------------------------------------------------
27027 // Is the node on any shared boundary where the receiver processor
27028 // is not involved?
27029
27030 // Now check if the node is on a shared boundary created by the
27031 // current processor (my_rank) and other processor different that
27032 // the iproc processor. This info. will help to complete the sending
27033 // of halo(ed) information between processors
27034
27035 // Flag to know if the node is on a shared boundary with other
27036 // processor
27037 bool node_on_shared_boundary_with_other_processors = false;
27038 // Count the number of other shared boundaries it could be on
27039 unsigned nshared_boundaries_with_other_processors_have_node = 0;
27040
27041 // Loop over the shared boundaries of the sent processor (my_rank)
27042 // and other processors (jproc)
27043 for (unsigned jproc = 0; jproc < nproc; jproc++)
27044 {
27045 // Do not search with the iproc processor, that was done before
27046 // above
27047 if (jproc != iproc)
27048 {
27049 // Get the number of shared boundaries with the jproc processor
27050 const unsigned n_jshd_bnd = this->nshared_boundaries(my_rank, jproc);
27051 // Loop over the shared boundaries
27052 for (unsigned bb = 0; bb < n_jshd_bnd; bb++)
27053 {
27054 // Get the boundary id
27055 const unsigned j_shd_bnd =
27056 this->shared_boundaries_ids(my_rank, jproc, bb);
27057 // Is the node part of this boundary?
27058 if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt))
27059 {
27060 // DEBP("Sending to");
27061 // DEBP(iproc);
27062 // DEBP("Pair of procs where other shared");
27063 // DEBP(my_rank);
27064 // DEBP(jproc);
27065 // DEBP(i_bnd);
27066 node_on_shared_boundary_with_other_processors = true;
27067 // Increase the counter for the number of shared boundaries
27068 // with other processors the node is on
27069 nshared_boundaries_with_other_processors_have_node++;
27070 } // if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt)
27071
27072 } // for (bb<n_jshd_bnd)
27073
27074 } // if (jproc != iproc)
27075
27076 } // for (jproc < nproc)
27077
27078 // If the node is on a shared boundary with another processor
27079 // (my_rank, jproc), then send the flag and look for the info.
27080 if (node_on_shared_boundary_with_other_processors)
27081 {
27082 Flat_packed_unsigneds.push_back(4);
27083#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27085 "Node is on shared boundary no related with the received processor: 4");
27086#endif
27087
27088 // The number of packages of information that will be sent to the
27089 // "iproc" processor. This helps to know how many packages of data
27090 // read from the received processor
27091 Flat_packed_unsigneds.push_back(
27092 nshared_boundaries_with_other_processors_have_node);
27093#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27094 std::stringstream junk;
27095 junk << "Number of other shared boundaries that the node is on: "
27096 << nshared_boundaries_with_other_processors_have_node;
27097 Flat_packed_unsigneds_string.push_back(junk.str());
27098#endif
27099
27100 // Counter to ensure that the correct number of data has been sent
27101 unsigned counter_shd_bnd_with_other_procs_have_node = 0;
27102 // Loop over the shared boundaries with other processors and get:
27103 // 1) The processors defining the shared boundary
27104 // 2) The shared boundary id
27105 // 3) The index of the node on the shared boundary
27106 Vector<unsigned> other_processor_1;
27107 Vector<unsigned> other_processor_2;
27108 Vector<unsigned> shd_bnd_ids;
27109 Vector<unsigned> indexes;
27110 // Loop over the processors again
27111 for (unsigned jproc = 0; jproc < nproc; jproc++)
27112 {
27113 // Do not search with the iproc processor, that was done before
27114 // above
27115 if (jproc != iproc)
27116 {
27117 // Get the number of shared boundaries with the jproc
27118 // processor
27119 const unsigned n_jshd_bnd = this->nshared_boundaries(my_rank, jproc);
27120 for (unsigned bb = 0; bb < n_jshd_bnd; bb++)
27121 {
27122 // Get the boundary id
27123 const unsigned j_shd_bnd =
27124 this->shared_boundaries_ids(my_rank, jproc, bb);
27125 // Is the node part of this boundary?
27126 if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt))
27127 {
27128 // Include the first processor
27129 other_processor_1.push_back(my_rank);
27130 // Include the second processor
27131 other_processor_2.push_back(jproc);
27132 // Include the shared boundary id
27133 shd_bnd_ids.push_back(j_shd_bnd);
27134 // Increase the counter for found shared boundaries with
27135 // other processors
27136 counter_shd_bnd_with_other_procs_have_node++;
27137 }
27138
27139 } // for (bb < nshared_bnd)
27140
27141 } // if (jproc != iproc)
27142
27143 } // for (jproc < nproc)
27144
27145 // Get the indexes of the node on all the shared boundaries where
27146 // it was found
27147 const unsigned n_other_processors = other_processor_1.size();
27148 // Loop over the processors where the node was found
27149 for (unsigned i = 0; i < n_other_processors; i++)
27150 {
27151 // Get the shared boundary id
27152 unsigned shd_bnd_id = shd_bnd_ids[i];
27153 // Get the number of nodes on that shared boundary
27154 const unsigned n_nodes_on_shd_bnd =
27155 nsorted_shared_boundary_node(shd_bnd_id);
27156
27157#ifdef PARANOID
27158 bool found_index_node_on_shared_boundary = false;
27159#endif
27160 for (unsigned i = 0; i < n_nodes_on_shd_bnd; i++)
27161 {
27162 // Get the i-th shared boundary node
27163 Node* shared_node_pt = sorted_shared_boundary_node_pt(shd_bnd_id, i);
27164 // Is the same node?
27165 if (shared_node_pt == nod_pt)
27166 {
27167 // DEBP(i_node);
27168 // DEBP(nod_pt->x(0));
27169 // DEBP(nod_pt->x(1));
27170 // Include the index of the node
27171 indexes.push_back(i);
27172#ifdef PARANOID
27173 // Mark as found the node
27174 found_index_node_on_shared_boundary = true;
27175#endif
27176 break;
27177 } // if (shared_node_pt == nod_pt)
27178
27179 } // for (i < n_nodes_on_shd_bnd)
27180
27181#ifdef PARANOID
27182 if (!found_index_node_on_shared_boundary)
27183 {
27184 std::ostringstream error_message;
27185 error_message << "The index of the node on boundary (" << shd_bnd_id
27186 << "), shared by other processors\nwas not found.\n"
27187 << "The node coordinates are (" << nod_pt->x(0) << ","
27188 << nod_pt->x(1) << ").\n";
27189 throw OomphLibError(
27190 error_message.str(),
27191 "RefineableTriangleMesh::get_required_nodal_information_helper()",
27192 OOMPH_EXCEPTION_LOCATION);
27193 }
27194#endif
27195 } // for (i < n_other_processors)
27196
27197 // Now send the info. but first check that the number of found
27198 // nodes be the same that the previously found shared boundaries
27199 // with the node
27200#ifdef PARANOID
27201 if (counter_shd_bnd_with_other_procs_have_node !=
27202 nshared_boundaries_with_other_processors_have_node)
27203 {
27204 std::ostringstream error_message;
27205 error_message << "The number of shared boundaries where the node is on "
27206 << "is different:\n"
27207 << "nshared_boundaries_with_other_processors_have_node: ("
27208 << nshared_boundaries_with_other_processors_have_node
27209 << ")\n"
27210 << "counter_shd_bnd_with_other_procs_have_node: ("
27211 << counter_shd_bnd_with_other_procs_have_node << ")\n";
27212 throw OomphLibError(
27213 error_message.str(),
27214 "RefineableTriangleMesh::get_required_nodal_information_helper()",
27215 OOMPH_EXCEPTION_LOCATION);
27216 } // if (counter_shd_bnd_with_other_procs_have_node !=
27217 // nshared_boundaries_with_other_processors_have_node)
27218#endif
27219
27220 // Loop over the info. to send it
27221 for (unsigned i = 0; i < n_other_processors; i++)
27222 {
27223 Flat_packed_unsigneds.push_back(other_processor_1[i]);
27224#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27225 std::stringstream junk1;
27226 junk1 << "Processor where the other shared boundary "
27227 << "has the node: " << other_processor_1[i];
27228 Flat_packed_unsigneds_string.push_back(junk1.str());
27229#endif
27230
27231 Flat_packed_unsigneds.push_back(other_processor_2[i]);
27232#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27233 std::stringstream junk2;
27234 junk2 << "Processor where the other shared boundary "
27235 << "has the node: " << other_processor_2[i];
27236 Flat_packed_unsigneds_string.push_back(junk2.str());
27237#endif
27238
27239 Flat_packed_unsigneds.push_back(shd_bnd_ids[i]);
27240#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27241 std::stringstream junk3;
27242 junk3 << "Other shared boundary id where the node is on"
27243 << boundaries[i];
27244 Flat_packed_unsigneds_string.push_back(junk3.str());
27245#endif
27246
27247 Flat_packed_unsigneds.push_back(indexes[i]);
27248#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27249 std::stringstream junk4;
27250 junk4 << "Node index on other shared boundary " << boundaries[i]
27251 << " is " << indexes[i];
27252 Flat_packed_unsigneds_string.push_back(junk4.str());
27253#endif
27254
27255 } // for (i < n_other_processors)
27256
27257 } // if (node_on_shared_boundary_with_other_processors)
27258 else
27259 {
27260 Flat_packed_unsigneds.push_back(0);
27261#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27263 "Node is on any shared boundary with other processors");
27264#endif
27265 } // else if (node_on_shared_boundary_with_other_processors)
27266
27267 // It may still be possible that the node be shared with the
27268 // processor that receives the info. but it is neither on shared
27269 // boundary with the receiver processor nor on a shared boundary
27270 // with others processors. Think in the next case:
27271
27272 // |-----|-----| - The elements in processor 3 need to be sent to
27273 // | 4 | 3 | processor 1, and that is all
27274 // |-----*-----| - When processor 1 receives the data from node (*)
27275 // | 1 | 2 | it just RE-CREATES it becasuse it is does not know
27276 // |-----|-----| that the node is also on the shared boundary that
27277 // processor 1 and 2 or processor 1 and 4 share.
27278
27279 // This problem become even worse if there would be more processors
27280 // between processor 3 and 2, or/and processor 3 and 4. Think in
27281 // triangles sharing the node (*)
27282
27283 // To solve this check if the node that we are trying to send is
27284 // part of the halo elements of the curreent processor (my_rank)
27285 // with any other processor (we need to check with all the
27286 // processors and not just with the processors to which we will send
27287 // to cover more cases)
27288
27289 // Store the halo element number with jproc where the node was found
27290 Vector<Vector<unsigned>> halo_element_number(nproc);
27291 // Store the node number on the halo element where the node was found
27292 Vector<Vector<unsigned>> halo_node_number_in_halo_element(nproc);
27293
27294 // Loop over the processor
27295 for (unsigned jproc = 0; jproc < nproc; jproc++)
27296 {
27297 // Get the number of halo elements with the jproc processor
27298 const unsigned n_halo_jproc = f_halo_ele_pt[jproc].size();
27299 // Loop over the halo elements
27300 for (unsigned jh = 0; jh < n_halo_jproc; jh++)
27301 {
27302 FiniteElement* halo_ele_pt = f_halo_ele_pt[jproc][jh];
27303 // Get the number of nodes of the halo element
27304 const unsigned n_node = halo_ele_pt->nnode();
27305 // Loop over the nodes
27306 for (unsigned n = 0; n < n_node; n++)
27307 {
27308 // Is the node part of the ih-th halo element with jproc
27309 if (nod_pt == halo_ele_pt->node_pt(n))
27310 {
27311 halo_element_number[jproc].push_back(jh);
27312 halo_node_number_in_halo_element[jproc].push_back(n);
27313 // break with the nodes, no need to look for more nodes in
27314 // the element
27315 break;
27316 } // if (nod_pt == halo_ele_pt->node_pt(n))
27317
27318 } // for (n < n_node)
27319
27320 } // for (jh < n_halo_jproc)
27321
27322 } // for (jproc < nproc)
27323
27324 // Send the info. related with if the node is on halo elements with
27325 // any processor
27326
27327 // Loop over the processors
27328 for (unsigned jproc = 0; jproc < nproc; jproc++)
27329 {
27330 // Get the number of halo elements with jproc processor where the
27331 // node is
27332 const unsigned n_jproc_halo_ele_node_is_on =
27333 halo_element_number[jproc].size();
27334 // Send the number of halo elements with jproc where the node is
27335 Flat_packed_unsigneds.push_back(n_jproc_halo_ele_node_is_on);
27336#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27337 std::stringstream junk5;
27338 junk5 << "Node is on " << n_jproc_halo_ele_node_is_on << " halo "
27339 << "elements with " << jproc << "-th processor";
27340 Flat_packed_unsigneds_string.push_back(junk5.str());
27341#endif
27342 // Send the halo elements indexes (which will be haloed elements
27343 // indexes in the receiver processor), and the indexes of the
27344 // nodes in each halo element
27345 for (unsigned i = 0; i < n_jproc_halo_ele_node_is_on; i++)
27346 {
27347 // The halo element index
27348 const unsigned halo_element_index = halo_element_number[jproc][i];
27349 Flat_packed_unsigneds.push_back(halo_element_index);
27350#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27351 std::stringstream junk6;
27352 junk6 << "Halo element index is (" << halo_element_index
27353 << ") with processor (" << jproc << ")";
27354 Flat_packed_unsigneds_string.push_back(junk6.str());
27355#endif
27356 // The node index on the halo element
27357 const unsigned node_index = halo_node_number_in_halo_element[jproc][i];
27358 Flat_packed_unsigneds.push_back(node_index);
27359#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27360 std::stringstream junk7;
27361 junk7 << "The node index on the halo element index is (" << node_index;
27362 Flat_packed_unsigneds_string.push_back(junk7.str());
27363#endif
27364
27365 } // for (i < n_jproc_halo_ele_node_is_on)
27366
27367 } // for (jproc < nproc)
27368
27369 // Now check if it is required to send the info. of the node. If the
27370 // node is not on a shared boundary with the iproc processor then we
27371 // need to send the info.
27372
27373 // Flag to indicate if it is on a halo element with the iproc
27374 // processor. If this flag is true then there is no need to send the
27375 // info. to create the node, in the receiver processor the info is
27376 // copied from the indicated haloed element-node
27377 bool on_halo_element_with_iproc_processor = false;
27378 if (halo_element_number[iproc].size() > 0)
27379 {
27380 on_halo_element_with_iproc_processor = true;
27381 } // if (halo_element_number[iproc].size() > 0)
27382
27383 // if (!node_on_shared_boundary)
27384 if (!node_on_shared_boundary && !on_halo_element_with_iproc_processor)
27385 {
27386 // Send all the info. to create it
27387
27388 // Is the Node algebraic? If so, send its ref values and
27389 // an indication of its geometric objects if they are stored
27390 // in the algebraic mesh
27391 AlgebraicNode* alg_nod_pt = dynamic_cast<AlgebraicNode*>(nod_pt);
27392 if (alg_nod_pt != 0)
27393 {
27394 // The external mesh should be algebraic
27395 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(this);
27396
27397 // Get default node update function ID
27398 unsigned update_id = alg_nod_pt->node_update_fct_id();
27399 Flat_packed_unsigneds.push_back(update_id);
27400#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27401 Flat_packed_unsigneds_string.push_back("Alg Node update id");
27402#endif
27403
27404 // Get reference values at default...
27405 unsigned n_ref_val = alg_nod_pt->nref_value();
27406 Flat_packed_unsigneds.push_back(n_ref_val);
27407#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27408 Flat_packed_unsigneds_string.push_back("Alg Node n ref values");
27409#endif
27410 for (unsigned i_ref_val = 0; i_ref_val < n_ref_val; i_ref_val++)
27411 {
27412 Flat_packed_doubles.push_back(alg_nod_pt->ref_value(i_ref_val));
27413 }
27414
27415 // Access geometric objects at default...
27416 unsigned n_geom_obj = alg_nod_pt->ngeom_object();
27417 Flat_packed_unsigneds.push_back(n_geom_obj);
27418#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27419 Flat_packed_unsigneds_string.push_back("Alg Node n geom objects");
27420#endif
27421 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
27422 {
27423 GeomObject* geom_obj_pt = alg_nod_pt->geom_object_pt(i_geom);
27424
27425 // Check this against the stored geometric objects in mesh
27426 unsigned n_geom_list = alg_mesh_pt->ngeom_object_list_pt();
27427
27428 // Default found index to zero
27429 unsigned found_geom_object = 0;
27430 for (unsigned i_list = 0; i_list < n_geom_list; i_list++)
27431 {
27432 if (geom_obj_pt == alg_mesh_pt->geom_object_list_pt(i_list))
27433 {
27434 found_geom_object = i_list;
27435 }
27436 }
27437 Flat_packed_unsigneds.push_back(found_geom_object);
27438#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27439 Flat_packed_unsigneds_string.push_back("Found geom object");
27440#endif
27441 }
27442 } // (if alg_nod_pt!=0)
27443
27444 // Is it a SolidNode?
27445 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(nod_pt);
27446 if (solid_nod_pt != 0)
27447 {
27448 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
27449 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
27450 {
27451 for (unsigned t = 0; t < n_prev; t++)
27452 {
27453 Flat_packed_doubles.push_back(
27454 solid_nod_pt->variable_position_pt()->value(t, i_val));
27455 }
27456 }
27457
27458 Vector<double> values_solid_node;
27459 solid_nod_pt->add_values_to_vector(values_solid_node);
27460 const unsigned nvalues_solid_node = values_solid_node.size();
27461 Flat_packed_unsigneds.push_back(nvalues_solid_node);
27462#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27463 std::stringstream junk;
27464 junk << "Number of values solid node: " << nvalues_solid_node;
27465 Flat_packed_unsigneds_string.push_back(junk.str());
27466#endif
27467 for (unsigned i = 0; i < nvalues_solid_node; i++)
27468 {
27469 Flat_packed_doubles.push_back(values_solid_node[i]);
27470 }
27471 }
27472
27473 // Finally copy info required for all node types
27474 for (unsigned i_val = 0; i_val < n_val; i_val++)
27475 {
27476 for (unsigned t = 0; t < n_prev; t++)
27477 {
27478 Flat_packed_doubles.push_back(nod_pt->value(t, i_val));
27479 }
27480 }
27481
27482 // Now do positions
27483 for (unsigned idim = 0; idim < n_dim; idim++)
27484 {
27485 for (unsigned t = 0; t < n_prev; t++)
27486 {
27487 Flat_packed_doubles.push_back(nod_pt->x(t, idim));
27488 // DEBP(nod_pt->x(t,idim));
27489 }
27490 }
27491
27492 } // if (!node_on_shared_boundary && !on_halo_element_with_iproc_processor)
27493 }
27494
27495 //======================================================================
27496 /// Helper function to create elements on the loop
27497 /// process based on the info received in
27498 /// send_and_received_elements_nodes_info
27499 //======================================================================
27500 template<class ELEMENT>
27502 unsigned& iproc,
27503 Vector<Vector<FiniteElement*>>& f_haloed_ele_pt,
27504 Vector<Vector<std::map<unsigned, FiniteElement*>>>&
27505 received_old_haloed_element_pt,
27506 Vector<FiniteElement*>& new_elements_on_domain,
27507 Vector<Node*>& new_nodes_on_domain,
27508 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
27509 other_proc_shd_bnd_node_pt,
27510 Vector<Vector<Vector<unsigned>>>& global_node_names,
27511 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
27512 Vector<Node*>& global_shared_node_pt)
27513 {
27514#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27516 << " Bool: New element needs to be constructed "
27518 << std::endl;
27519#endif
27520
27522 {
27523 // Create a new element from the communicated values
27524 // and coords from the process that located zeta
27525 GeneralisedElement* new_el_pt = new ELEMENT;
27526
27527 // Add the new element to the mesh - Do not add the element yet
27528 // since no retained elements still need to be deleted
27529 // this->add_element_pt(new_el_pt);
27530
27531 // Cast to the FE pointer
27532 FiniteElement* f_el_pt = dynamic_cast<FiniteElement*>(new_el_pt);
27533
27534 // Add the element to the new elements in the domain container
27535 new_elements_on_domain.push_back(f_el_pt);
27536
27537 // Set any additional information for the element
27538 this->add_element_load_balance_helper(
27539 iproc, received_old_haloed_element_pt, f_el_pt);
27540
27541 // Add nodes to the new element
27542 unsigned n_node = f_el_pt->nnode();
27543 for (unsigned j = 0; j < n_node; j++)
27544 {
27545 Node* new_nod_pt = 0;
27546
27547 // Call the add halo node helper function
27548 add_received_node_load_balance_helper(new_nod_pt,
27549 f_haloed_ele_pt,
27550 received_old_haloed_element_pt,
27551 new_nodes_on_domain,
27552 other_proc_shd_bnd_node_pt,
27553 iproc,
27554 j,
27555 f_el_pt,
27556 global_node_names,
27557 node_name_to_global_index,
27558 global_shared_node_pt);
27559 }
27560 }
27561 else // the element already exists
27562 {
27563#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27565 << " Index of existing element "
27567 << std::endl;
27568#endif
27569
27570 // Incrase the index, we do anything else with the element
27572
27573 } // else the element already exists
27574 }
27575
27576 //========start of add_element_load_balance_helper=====================
27577 /// Helper function to create elements on the loop
27578 /// process based on the info received in
27579 /// send_and_received_elements_nodes_info
27580 /// This function is in charge of verify if the element is associated
27581 /// to a boundary and associate to it if that is the case
27582 //======================================================================
27583 template<class ELEMENT>
27585 const unsigned& iproc,
27586 Vector<Vector<std::map<unsigned, FiniteElement*>>>&
27587 received_old_haloed_element_pt,
27588 FiniteElement* ele_pt)
27589 {
27590 // Get the number of processors
27591 const unsigned nproc = this->communicator_pt()->nproc();
27592
27593#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27595 << " Bool: Element is associated to a boundary "
27597 << std::endl;
27598#endif
27599
27600 // Is on an original boundary?
27601 const unsigned is_on_original_boundary =
27603 if (is_on_original_boundary == 1)
27604 {
27605#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27607 << " How many boundaries are associated with the element "
27609 << std::endl;
27610#endif
27611 // Number of boundaries the element is associated with
27612 const unsigned nassociated_boundaries =
27614
27615 // Loop over the associated boundaries
27616 for (unsigned b = 0; b < nassociated_boundaries; b++)
27617 {
27618#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27620 << " Boundary associated to the element "
27622 << std::endl;
27623#endif
27624
27625 // The boundary id
27626 const unsigned bnd =
27628
27629#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27631 << " Face index of the element "
27633 << std::endl;
27634#endif
27635
27636 // The face index
27637 const unsigned face_index =
27639
27640 // Associate the element with the boundary and establish as many
27641 // face indexes it has
27642 this->Boundary_element_pt[bnd].push_back(ele_pt);
27643 this->Face_index_at_boundary[bnd].push_back(face_index);
27644
27645 } // (b < nassociated_boundaries)
27646
27647 // Here read the info. regarding the boundary-region of the element
27648#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27650 << " Bool: Element is associated to a boundary-region "
27652 << std::endl;
27653#endif
27654
27655 // Is the element associated to a boundary-region?
27657 {
27658#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27661 << " How many boundaries-regions are associated with the element "
27663 << std::endl;
27664#endif
27665 // Number of boundary-regions the element is associated
27666 const unsigned nassociated_boundaries_and_regions =
27668
27669 for (unsigned br = 0; br < nassociated_boundaries_and_regions; br++)
27670 {
27671#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27673 << " Boundary associated to the element "
27675 << std::endl;
27676#endif
27677 // The boundary id
27678 const unsigned bnd =
27680
27681#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27683 << " Region associated to the element "
27685 << std::endl;
27686#endif
27687 // The region id
27688 const unsigned region =
27690
27691#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27693 << " Face index of the element in boundary-region "
27695 << std::endl;
27696#endif
27697 const unsigned face_index =
27699
27700 // Associate the element with the boundary-regions and establish
27701 // as many face indexes it has
27702 this->Boundary_region_element_pt[bnd][region].push_back(ele_pt);
27703 this->Face_index_region_at_boundary[bnd][region].push_back(
27704 face_index);
27705
27706 } // for (br < nassociated_boundaries_and_regions)
27707
27708 } // Is the element associated with a boundary-region?
27709
27710 } // The element is associated with an original boundary
27711#ifdef PARANOID
27712 else
27713 {
27714 if (is_on_original_boundary != 0)
27715 {
27716 std::ostringstream error_message;
27717 error_message
27718 << "The current element is not on an original boundary, this should\n"
27719 << "be indicated by a zero flag. However, the read value for\n"
27720 << "that flag is (" << is_on_original_boundary << ").\n\n";
27721 throw OomphLibError(
27722 error_message.str(),
27723 "RefineableTriangleMesh::add_element_load_balance_helper()",
27724 OOMPH_EXCEPTION_LOCATION);
27725 } // if (is_on_shared_boundary != 0)
27726 }
27727#endif
27728
27729#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27731 << " Bool: Element is associated to a shared boundary "
27733 << std::endl;
27734#endif
27735
27736 // Is the element a shared boundary element?
27737 const unsigned is_on_shared_boundary =
27739 if (is_on_shared_boundary == 3)
27740 {
27741#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27744 << " How many shared boundaries are associated with the element "
27746 << std::endl;
27747#endif
27748
27749 // The number of shared boundaries the element is associated
27750 const unsigned nassociated_shared_boundaries =
27752
27753 // Loop over the associated shared boundaries
27754 for (unsigned b = 0; b < nassociated_shared_boundaries; b++)
27755 {
27756#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27758 << " Shared boundary associated to the element "
27760 << std::endl;
27761#endif
27762 const unsigned bnd =
27764
27765#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27768 << " Face index of the element associated to the shared boundary "
27770 << std::endl;
27771#endif
27772
27773 const unsigned face_index =
27775
27776 this->add_shared_boundary_element(bnd, ele_pt);
27777 this->add_face_index_at_shared_boundary(bnd, face_index);
27778
27779 } // (b < nassociated_shared_boundaries)
27780
27781 } // The element is associted with a shared boundary
27782#ifdef PARANOID
27783 else
27784 {
27785 if (is_on_shared_boundary != 0)
27786 {
27787 std::ostringstream error_message;
27788 error_message
27789 << "The current element is not on a shared boundary, this should\n"
27790 << "be indicated by a zero flag. However, the read value for\n"
27791 << "that flag is (" << is_on_shared_boundary << ").\n\n";
27792 throw OomphLibError(
27793 error_message.str(),
27794 "RefineableTriangleMesh::add_element_load_balance_helper()",
27795 OOMPH_EXCEPTION_LOCATION);
27796 } // if (is_on_shared_boundary != 0)
27797 }
27798#endif
27799
27800 // Now check if the element is a haloed element in the sender
27801 // processor with any other processor
27802
27803 // Loop over the processors
27804 for (unsigned jproc = 0; jproc < nproc; jproc++)
27805 {
27806#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27808 << " Bool: Number of haloed indexes of the element with the "
27809 << jproc << " processor: "
27811 << std::endl;
27812#endif
27813 // Is the element haloed with the jproc processor
27814 const unsigned n_index_haloed_jproc =
27816 // Loop over the number of haloed indexes
27817 for (unsigned ihd = 0; ihd < n_index_haloed_jproc; ihd++)
27818 {
27819#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27821 << " Bool: The haloed element index with the " << jproc
27822 << " processor: "
27824 << std::endl;
27825#endif
27826 const unsigned haloed_index =
27828
27829 // Set the halod element in the proper storage
27830 received_old_haloed_element_pt[iproc][jproc][haloed_index] = ele_pt;
27831
27832 } // for (ihd < n_index_haloed_jproc)
27833
27834 } // for (jproc < nproc)
27835 }
27836
27837 //======================================================================
27838 /// Helper function to add a new node from load balance
27839 //======================================================================
27840 template<class ELEMENT>
27842 Node*& new_nod_pt,
27843 Vector<Vector<FiniteElement*>>& f_haloed_ele_pt,
27844 Vector<Vector<std::map<unsigned, FiniteElement*>>>&
27845 received_old_haloed_element_pt,
27846 Vector<Node*>& new_nodes_on_domain,
27847 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
27848 other_proc_shd_bnd_node_pt,
27849 unsigned& iproc,
27850 unsigned& node_index,
27851 FiniteElement* const& new_el_pt,
27852 Vector<Vector<Vector<unsigned>>>& global_node_names,
27853 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
27854 Vector<Node*>& global_shared_node_pt)
27855 {
27856 // Given the node, received information about it from processor
27857 // iproc, construct it on the current process
27858#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27860 << " Bool: New node needs to be constructed "
27862 << std::endl;
27863#endif
27865 {
27866 // Construct a new node based upon sent information, or copy a node
27867 // from one of the shared boundaries
27868 construct_new_node_load_balance_helper(new_nod_pt,
27869 f_haloed_ele_pt,
27870 received_old_haloed_element_pt,
27871 new_nodes_on_domain,
27872 other_proc_shd_bnd_node_pt,
27873 iproc,
27874 node_index,
27875 new_el_pt,
27876 global_node_names,
27877 node_name_to_global_index,
27878 global_shared_node_pt);
27879 }
27880 else
27881 {
27882#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27884 << " Index of existing halo node "
27886 << std::endl;
27887#endif
27888 // The node already exist, copy it from the indicated position
27889
27890 // Get the node's index, and copy it
27891 new_nod_pt = new_nodes_on_domain
27893
27894 // Set the node in the current element
27895 new_el_pt->node_pt(node_index) = new_nod_pt;
27896 }
27897 }
27898
27899 //============start_of_construct_new_node_load_balance_helper()=========
27900 /// Helper function which constructs a new node (on an
27901 /// element) with the information sent from the load balance
27902 /// process
27903 //======================================================================
27904 template<class ELEMENT>
27906 Node*& new_nod_pt,
27907 Vector<Vector<FiniteElement*>>& f_haloed_ele_pt,
27908 Vector<Vector<std::map<unsigned, FiniteElement*>>>&
27909 received_old_haloed_element_pt,
27910 Vector<Node*>& new_nodes_on_domain,
27911 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
27912 other_proc_shd_bnd_node_pt,
27913 unsigned& iproc,
27914 unsigned& node_index,
27915 FiniteElement* const& new_el_pt,
27916 Vector<Vector<Vector<unsigned>>>& global_node_names,
27917 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
27918 Vector<Node*>& global_shared_node_pt)
27919 {
27920 // Get the number of processors
27921 const unsigned nproc = this->communicator_pt()->nproc();
27922 // Get the rank of the current processor
27923 const unsigned my_rank = this->communicator_pt()->my_rank();
27924
27925 // The first entry indicates the number of values at this new Node
27926 //(which may be different across the same element e.g. Lagrange multipliers)
27927#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27929 << " Number of values of external halo node "
27931 << std::endl;
27932#endif
27934
27935 // Null TimeStepper for now
27936 TimeStepper* time_stepper_pt = this->Time_stepper_pt;
27937 // Default number of previous values to 1
27938 unsigned n_prev = time_stepper_pt->ntstorage();
27939
27940 // ------------------------------------------------------
27941 // Check if the node is on an original boundary
27942#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27944 << " Is the node on an original boundary "
27946 << std::endl;
27947#endif
27948
27949 // Flag to indicate if the node is on original boundaries
27950 const unsigned node_on_original_boundaries =
27952
27953 // Store the original boundaries where the node is on
27954 Vector<unsigned> original_boundaries_node_is_on;
27955 // Store the zeta coordinates of the node on the original boundaries
27956 Vector<double> zeta_coordinates;
27957 // Store the number of original boundaries the node is on
27958 unsigned n_original_boundaries_node_is_on = 0;
27959
27960 if (node_on_original_boundaries == 2)
27961 {
27962 // How many original boundaries does the node live on?
27963#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27965 << " Number of boundaries the node is on: "
27967 << std::endl;
27968#endif
27969 n_original_boundaries_node_is_on =
27971
27972 // Resize the containers
27973 original_boundaries_node_is_on.resize(n_original_boundaries_node_is_on);
27974 zeta_coordinates.resize(n_original_boundaries_node_is_on);
27975
27976 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
27977 {
27978 // Boundary number
27979#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27981 << " Node is on boundary "
27983 << std::endl;
27984#endif
27985 original_boundaries_node_is_on[i] =
27987 zeta_coordinates[i] =
27989 }
27990
27991 } // if (node_on_original_boundaries==2)
27992#ifdef PARANOID
27993 else
27994 {
27995 if (node_on_original_boundaries != 0)
27996 {
27997 std::ostringstream error_message;
27998 error_message
27999 << "The current node is not on an original boundary, this should\n"
28000 << "be indicated by a zero flag. However, the read value for\n"
28001 << "that flag is (" << node_on_original_boundaries << ").\n\n";
28002 throw OomphLibError(
28003 error_message.str(),
28004 "RefineableTriangleMesh::construct_new_halo_node_helper()",
28005 OOMPH_EXCEPTION_LOCATION);
28006 } // if (node_on_original_boundaries != 0)
28007 }
28008#endif
28009
28010 // --------------------------------------------------------------
28011 // Check if the node was on a shared boundary with the iproc
28012 // processor
28013#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28015 << " Is node on shared boundary? "
28017 << std::endl;
28018#endif
28019 const unsigned is_node_on_shared_boundary =
28021 if (is_node_on_shared_boundary == 1)
28022 {
28023 // How many shared boundaries does the node live on?
28024#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28026 << " Number of boundaries the node is on: "
28028 << std::endl;
28029#endif
28030 const unsigned n_shd_bnd_node_is_on =
28032 Vector<unsigned> shd_bnds_node_is_on(n_shd_bnd_node_is_on);
28033 for (unsigned i = 0; i < n_shd_bnd_node_is_on; i++)
28034 {
28035 // Shared boundary number
28036#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28038 << " Node is on boundary "
28040 << std::endl;
28041#endif
28042 shd_bnds_node_is_on[i] =
28044 }
28045
28046 // Get the index of the node on the shared boundary
28047#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28049 << " Index of node on boundary "
28051 << std::endl;
28052#endif
28053 // Get the node index of the node on the shared boundary
28054 unsigned node_index_on_shared_boundary =
28056
28057 // Get the pointer to the node with the received info.
28058 new_nod_pt = this->sorted_shared_boundary_node_pt(
28059 shd_bnds_node_is_on[0], node_index_on_shared_boundary);
28060
28061 } // if (is_node_on_shared_boundary == 1)
28062#ifdef PARANOID
28063 else
28064 {
28065 if (is_node_on_shared_boundary != 0)
28066 {
28067 std::ostringstream error_message;
28068 error_message
28069 << "The current node is not on a shared boundary, this should\n"
28070 << "be indicated by a zero flag. However, the read value for\n"
28071 << "that flag is (" << is_node_on_shared_boundary << ").\n\n";
28072 throw OomphLibError(
28073 error_message.str(),
28074 "RefineableTriangleMesh::construct_new_halo_node_helper()",
28075 OOMPH_EXCEPTION_LOCATION);
28076 } // if (node_on_shared_boundary != 0)
28077 }
28078#endif
28079
28080 // ------------------------------------------------------------
28081 // Is the node on a shared boundary with other processor?
28082#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28084 << " Is the node on shared boundaries with other processors "
28086 << std::endl;
28087#endif
28088
28089 // Is the node in shared boundaries no associated with the
28090 // receiver processor
28091 const unsigned is_the_node_in_shared_boundaries_with_other_processors =
28093
28094 // The containers where to store the info.
28095 Vector<unsigned> other_processor_1;
28096 Vector<unsigned> other_processor_2;
28097 Vector<unsigned> other_shared_boundaries;
28098 Vector<unsigned> other_indexes;
28099
28100 // How many shared bounaries with other processors the node lives on
28101 unsigned n_shd_bnd_with_other_procs_have_node = 0;
28102
28103 // Is the node on shared boundaries with other processors
28104 if (is_the_node_in_shared_boundaries_with_other_processors == 4)
28105 {
28106#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28108 << " In how many shared boundaries with other "
28109 << "processors is the node "
28111 << std::endl;
28112#endif
28113
28114 // How many nodes on other shared boundaries were found
28115 n_shd_bnd_with_other_procs_have_node =
28117
28118 // Resize the containers
28119 other_processor_1.resize(n_shd_bnd_with_other_procs_have_node);
28120 other_processor_2.resize(n_shd_bnd_with_other_procs_have_node);
28121 other_shared_boundaries.resize(n_shd_bnd_with_other_procs_have_node);
28122 other_indexes.resize(n_shd_bnd_with_other_procs_have_node);
28123
28124 for (unsigned i = 0; i < n_shd_bnd_with_other_procs_have_node; i++)
28125 {
28126#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28128 << " Processor where the other shared boundary"
28129 << "has the node"
28131 << std::endl;
28132#endif
28133 // Read the other processor 1
28134 other_processor_1[i] =
28136
28137#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28139 << " Processor where the other shared boundary"
28140 << "has the node"
28142 << std::endl;
28143#endif
28144 // Read the other processor 2
28145 other_processor_2[i] =
28147
28148#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28150 << " Other shared boundary id where the node is on: "
28152 << std::endl;
28153#endif
28154
28155 // Read the other shared boundary id
28156 other_shared_boundaries[i] =
28158
28159#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28161 << " Node index on the other shared boundary "
28163 << std::endl;
28164#endif
28165
28166 // Read the node index on the other shared boundary
28167 other_indexes[i] =
28169
28170 } // for (i < n_shd_bnd_with_other_procs_have_node)
28171
28172 } // if (is_the_node_in_shared_boundaries_with_other_processors == 4)
28173#ifdef PARANOID
28174 else
28175 {
28176 if (is_the_node_in_shared_boundaries_with_other_processors != 0)
28177 {
28178 std::ostringstream error_message;
28179 error_message
28180 << "The current node is not on a shared boundary with\n"
28181 << "other processors, this should be indicated by a zero flag.\n"
28182 << "However, the read value for that flag is ("
28183 << is_the_node_in_shared_boundaries_with_other_processors << ").\n\n";
28184 throw OomphLibError(
28185 error_message.str(),
28186 "RefineableTriangleMesh::construct_new_node_load_balance_helper()",
28187 OOMPH_EXCEPTION_LOCATION);
28188 }
28189 }
28190#endif
28191
28192 // ------------------------------------------------------------
28193 // Receive the info. to check if the node is on a haloed element
28194 // with any processor
28195
28196 // Store the halo element number with jproc where the node was found
28197 Vector<Vector<unsigned>> halo_element_number(nproc);
28198 // Store the node number on the halo element where the node was found
28199 Vector<Vector<unsigned>> halo_node_number_in_halo_element(nproc);
28200
28201 // Loop over the processors
28202 for (unsigned jproc = 0; jproc < nproc; jproc++)
28203 {
28204#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28206 << " The node is on "
28208 << " halo elements with " << jproc << " processor"
28209 << std::endl;
28210#endif
28211 // Get the number of halo elements with jproc processor where the
28212 // node was found
28213 const unsigned n_jproc_halo_ele_node_is_on =
28215
28216 // Resize the containers
28217 halo_element_number[jproc].resize(n_jproc_halo_ele_node_is_on);
28218 halo_node_number_in_halo_element[jproc].resize(
28219 n_jproc_halo_ele_node_is_on);
28220
28221 // Read halo elements indexes (which are indexes of the halo
28222 // elements of the sender processor (iproc) with other processors
28223 // (included my_rank)
28224 for (unsigned i = 0; i < n_jproc_halo_ele_node_is_on; i++)
28225 {
28226 // Get the halo element index in the jproc processor
28227#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28229 << " The halo element index where the node is on "
28231 << std::endl;
28232#endif
28233 // Get the node index on the halo element
28234 const unsigned halo_ele_index =
28236#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28238 << " The node index on the halo element where the node "
28239 << "is on "
28241 << std::endl;
28242#endif
28243 const unsigned node_index_on_halo_ele =
28245
28246 // Store the halo element number
28247 halo_element_number[jproc][i] = halo_ele_index;
28248 // Store the index of on the haloed element
28249 halo_node_number_in_halo_element[jproc][i] = node_index_on_halo_ele;
28250
28251 } // for (i < n_jproc_halo_ele_node_is_on)
28252
28253 } // for (jproc < nproc)
28254
28255 // Store the node pointers obtained from the indicated halo elements
28256 // (use a set to check for the case when the node pointer is
28257 // different)
28258 std::set<Node*> set_haloed_node_pt;
28259
28260 // Store the node pointer obtained from the haloed elements
28261 Node* haloed_node_pt = 0;
28262
28263 // Flag to indicate if it is on a haloed element of the current
28264 // processor with the iproc processor. If this flag is true then
28265 // there is no need to read the info. to create the node, only copy
28266 // the node from the indicated haloed element-node
28267 bool on_haloed_element_with_iproc_processor = false;
28268 if (halo_element_number[my_rank].size() > 0)
28269 {
28270 // The node is part of the haloed element in the current processor
28271 // (my_rank) with the receiver processor
28272 on_haloed_element_with_iproc_processor = true;
28273
28274 // Get the number of haloed elements in the current processor
28275 const unsigned n_haloed_indexes = halo_element_number[my_rank].size();
28276 // Loop over the different haloed indexes, and get the nodes
28277 // instances from all the indicated haloed elements (all of them
28278 // should be the same)
28279 for (unsigned i = 0; i < n_haloed_indexes; i++)
28280 {
28281 // Get the haloed element numbers where the node is on
28282 const unsigned haloed_index = halo_element_number[my_rank][i];
28283 // Get the node index on the haloed element
28284 const unsigned haloed_node_index =
28285 halo_node_number_in_halo_element[my_rank][i];
28286
28287 // Get the haloed element (with iproc)
28288 FiniteElement* tmp_haloed_ele_pt = f_haloed_ele_pt[iproc][haloed_index];
28289 // Get the node on the indicated node number
28290 Node* tmp_haloed_node_pt =
28291 tmp_haloed_ele_pt->node_pt(haloed_node_index);
28292
28293 // Set the pointer for the obtained haloed node
28294 haloed_node_pt = tmp_haloed_node_pt;
28295
28296 // Add the node to the set of node pointers
28297 set_haloed_node_pt.insert(tmp_haloed_node_pt);
28298
28299#ifdef PARANOID
28300 if (set_haloed_node_pt.size() > 1)
28301 {
28302 std::ostringstream error_message;
28303 error_message
28304 << "When adding the " << haloed_node_index << " node of the "
28305 << haloed_index << "-th haloed element\n"
28306 << "in the currrent processor with the " << iproc << " processor"
28307 << "it was found that\nthe node pointer is different from the other"
28308 << "instances of the node.\nIt means we have a repeated node."
28309 << "This are the node coordinates of the previous node instances\n"
28310 << "The last entry is for the just added node with a different "
28311 "node\n"
28312 << "pointer\n";
28313 for (std::set<Node*>::iterator it = set_haloed_node_pt.begin();
28314 it != set_haloed_node_pt.end();
28315 it++)
28316 {
28317 error_message << "Node: (" << (*it)->x(0) << ", " << (*it)->x(1)
28318 << ")\n";
28319 }
28320 error_message << "\n";
28321 throw OomphLibError(
28322 error_message.str(),
28323 "RefineableTriangleMesh::construct_new_node_load_balance_helper()",
28324 OOMPH_EXCEPTION_LOCATION);
28325 }
28326#endif
28327
28328 } // for (i < n_haloed_indexes)
28329
28330 } // if (halo_element_number[iproc].size() > 0)
28331
28332 // Flag to indicate if the node has been found on a haloed element
28333 // of other processor with the iproc processor
28334 bool found_on_haloed_element_with_other_processor = false;
28335 // Loop over the processors (only until the iproc since no info. of
28336 // higher processors has been received)
28337 for (unsigned jproc = 0; jproc < iproc; jproc++)
28338 {
28339 // Is the node on a halo element with the jproc processor
28340 if (halo_element_number[jproc].size() > 0)
28341 {
28342 // Get the number of halo elements with the jproc processor
28343 const unsigned n_halo_indexes = halo_element_number[jproc].size();
28344 // Loop over the different halo indexes, and get the nodes
28345 // instances from all the indicated halo elements (all of them
28346 // should be the same)
28347 for (unsigned i = 0; i < n_halo_indexes; i++)
28348 {
28349 // Get the haloed element numbers where the node is on
28350 const unsigned haloed_index = halo_element_number[jproc][i];
28351 // Get the node index on the haloed element
28352 const unsigned haloed_node_index =
28353 halo_node_number_in_halo_element[jproc][i];
28354
28355 // Have we received the indicated element? (Get the haloed
28356 // element on jproc with the iproc processor)
28357 std::map<unsigned, FiniteElement*>::iterator it_map =
28358 received_old_haloed_element_pt[jproc][iproc].find(haloed_index);
28359 // Have we received the indicated element?
28360 if (it_map != received_old_haloed_element_pt[jproc][iproc].end())
28361 {
28362 // Set the flag of found element in other processors haloed
28363 // element, in this case in haloed elements of processor
28364 // jproc wiht iproc processor
28365 found_on_haloed_element_with_other_processor = true;
28366
28367 // Get the element
28368 FiniteElement* tmp_haloed_ele_pt = (*it_map).second;
28369 // Get the node on the indicated node number
28370 Node* tmp_haloed_node_pt =
28371 tmp_haloed_ele_pt->node_pt(haloed_node_index);
28372
28373 // Set the pointer for the obtained haloed node
28374 haloed_node_pt = tmp_haloed_node_pt;
28375
28376 // Add the node to the set of node pointers
28377 set_haloed_node_pt.insert(tmp_haloed_node_pt);
28378
28379#ifdef PARANOID
28380 if (set_haloed_node_pt.size() > 1)
28381 {
28382 std::ostringstream error_message;
28383 error_message
28384 << "When adding the " << haloed_node_index << " node of the "
28385 << haloed_index << "-th haloed element "
28386 << "of the " << jproc << " processor\nwith the " << iproc
28387 << " processor, it was found that\n"
28388 << "the node pointer is different from the other\n"
28389 << "instances of the node.\nThis means we have a repeated "
28390 "node.\n"
28391 << "These are the node coordinates of the previous node "
28392 << "instances\n"
28393 << "The last entry is for the just added node with a "
28394 "different\n"
28395 << "node pointer\n";
28396 for (std::set<Node*>::iterator it = set_haloed_node_pt.begin();
28397 it != set_haloed_node_pt.end();
28398 it++)
28399 {
28400 error_message << "Node: (" << (*it)->x(0) << ", " << (*it)->x(1)
28401 << ")\n";
28402 }
28403 error_message << "\n";
28404 throw OomphLibError(error_message.str(),
28405 "RefineableTriangleMesh::construct_new_node_"
28406 "load_balance_helper()",
28407 OOMPH_EXCEPTION_LOCATION);
28408 }
28409#endif
28410
28411 } // if (it_map != received_old_haloed_element_pt[jproc][iproc].end())
28412 // Have we received the element?
28413
28414 } // for (i < n_haloed_indexes)
28415
28416 } // if (halo_element_number[iproc].size() > 0)
28417
28418 } // for (jproc < nproc)
28419
28420 // If the node was found in the haloed elements of the current
28421 // processor with the iproc processor, or in the haloed elements of
28422 // any other processor with the iproc processor then copy the node
28423 // pointer (no problem if we overwrite the node info. it should be
28424 // the same node pointer)
28425 if (on_haloed_element_with_iproc_processor ||
28426 found_on_haloed_element_with_other_processor)
28427 {
28428 // Set the node pointer
28429 new_nod_pt = haloed_node_pt;
28430 }
28431
28432 // Now we have all the info. to decide if the node should be created
28433 // or not
28434
28435 // First check if the node is a shared boundary node, or if it has
28436 // been found on haloed elements
28437 if (is_node_on_shared_boundary == 1 ||
28438 (on_haloed_element_with_iproc_processor))
28439 {
28440 // We already have the node, we do not need to create it
28441
28442 // Only check if we need to add boundary info. to the node
28443 if (node_on_original_boundaries == 2)
28444 {
28445 // The node is a boundary node, add the boundary info. before
28446 // adding it to the domain
28447
28448 // Associate the node to the given boundaries
28449 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
28450 {
28451 add_boundary_node(original_boundaries_node_is_on[i], new_nod_pt);
28452 // Establish the boundary coordinates for the node
28453 Vector<double> zeta(1);
28454 zeta[0] = zeta_coordinates[i];
28455 new_nod_pt->set_coordinates_on_boundary(
28456 original_boundaries_node_is_on[i], zeta);
28457 }
28458
28459 } // if (node_on_original_boundaries==2)
28460
28461 // Add the node to the domain
28462 new_nodes_on_domain.push_back(new_nod_pt);
28463
28464 // Add the node to the element
28465 new_el_pt->node_pt(node_index) = new_nod_pt;
28466
28467 } // if (is_node_on_shared_boundary == 1)
28468
28469 // Now check if the node is on a shared boundary with another
28470 // processor, if that is the case try to find the node that may have
28471 // been already sent by the other processors
28472
28473 // This flags indicates if the node was found, and then decide if it
28474 // is required to create the node
28475 bool found_node_in_other_shared_boundaries = false;
28476 // Flag to indicate whether the node should be created as a boundary
28477 // node or not. If the node lies on a shared boundary with other
28478 // processor the we create it as a boundary node. The processor from
28479 // which we are receiving info. (iproc) may not know that the node
28480 // lies on an original boundary. If the node lies on an original
28481 // boundary then its info. will be sent by another processor, then
28482 // we can set its boundary info. since the node was constructed as a
28483 // boundary node
28484 bool build_node_as_boundary_node = false;
28485
28486 if (is_the_node_in_shared_boundaries_with_other_processors == 4)
28487 {
28488 // Build the node as a boundary node
28489 build_node_as_boundary_node = true;
28490
28491 // Try to get the node pointer in case that the node has been
28492 // already sent by the other processors
28493
28494 // Get the number of initial shared boundaries to correct the
28495 // index of the shared boundary
28496 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
28497
28498 // Add the found nodes in the container, should be the same but
28499 // better check
28500 Vector<Node*> found_node_pt;
28501
28502 // Now try to find the node in any of the other shared boundaries
28503 for (unsigned i = 0; i < n_shd_bnd_with_other_procs_have_node; i++)
28504 {
28505 unsigned oproc1 = other_processor_1[i];
28506 unsigned oproc2 = other_processor_2[i];
28507
28508 // Check that we always check with the lower processors number
28509 // first
28510 if (oproc1 > oproc2)
28511 {
28512 oproc2 = oproc1;
28513 oproc1 = other_processor_2[i];
28514 } // if (oproc1 > oproc2)
28515
28516 // Re-compute the shared boundary id between the other
28517 // processors
28518 const unsigned shd_bnd_id =
28519 other_shared_boundaries[i] - initial_shd_bnd_id;
28520 // Read the index
28521 const unsigned index = other_indexes[i];
28522
28523 // Check if there are nodes received from the other processor
28524 // and with the given shared boundary
28525 const unsigned n_nodes_on_other_processor =
28526 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].size();
28527
28528 if (n_nodes_on_other_processor > 0)
28529 {
28530 // Check if we can find the index of the node in that other
28531 // processor and shared boundary id
28532 std::map<unsigned, Node*>::iterator it =
28533 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].find(index);
28534
28535 // If the index exist then get the node pointer
28536 if (it !=
28537 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].end())
28538 {
28539 // Mark the node as found
28540 found_node_in_other_shared_boundaries = true;
28541 // Get the node pointer
28542 Node* tmp_node_pt =
28543 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id][index];
28544 found_node_pt.push_back(tmp_node_pt);
28545 } // if (it!=
28546 // other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].end())
28547
28548 } // if (n_nodes_on_other_processor > 0)
28549
28550 } // for (i < n_shd_bnd_with_other_procs_have_node)
28551
28552 // If the node was found, then all their instances should be the
28553 // same but better check
28554 if (found_node_in_other_shared_boundaries)
28555 {
28556#ifdef PARANOID
28557 const unsigned ntimes_node_found = found_node_pt.size();
28558 for (unsigned j = 1; j < ntimes_node_found; j++)
28559 {
28560 if (found_node_pt[j - 1] != found_node_pt[j])
28561 {
28562 std::ostringstream error_message;
28563 error_message
28564 << "The instances of the node that was found to be on a\n"
28565 << "shared boundary with other processors are not the same,\n"
28566 << "the coordinates for the nodes are these:\n"
28567 << "(" << found_node_pt[j - 1]->x(0) << ", "
28568 << found_node_pt[j - 1]->x(1) << ")\n"
28569 << "(" << found_node_pt[j]->x(0) << ", " << found_node_pt[j]->x(1)
28570 << ")\n"
28571 << "Not be surprised if they are the same since the node is\n"
28572 << "repeated!!!\n";
28573 throw OomphLibError(
28574 error_message.str(),
28575 "RefineableTriangleMesh::construct_new_halo_node_helper()",
28576 OOMPH_EXCEPTION_LOCATION);
28577
28578 } // if (found_node_pt[j-1] != found_node_pt[j])
28579
28580 } // for (j < ntimes_node_found)
28581#endif
28582
28583 // Check if the node is a shared boundary node from the current
28584 // processor and the iproc processor, if that is the case, and
28585 // the node is also on a shared boundary with other processor,
28586 // then the pointer should be the same!!!
28587 if (is_node_on_shared_boundary == 1)
28588 {
28589 // The pointer to the node is already assigned, it was
28590 // assigned when thenode was found to be on a shared boundary
28591 // with the iproc processor
28592 if (found_node_pt[0] != new_nod_pt)
28593 {
28594 std::ostringstream error_message;
28595 error_message
28596 << "The pointer of the node that was found to be on a\n"
28597 << "shared boundary with other processor(s) and the pointer\n"
28598 << "of the node on shared boundary with the receiver\n"
28599 << "processor (iproc) are not the same. This means we have a\n"
28600 << "repeated node)\n"
28601 << "The coordinates for the nodes are:\n"
28602 << "(" << found_node_pt[0]->x(0) << ", " << found_node_pt[0]->x(1)
28603 << ")\n"
28604 << "(" << new_nod_pt->x(0) << ", " << new_nod_pt->x(1) << ")\n"
28605 << "Not to be surprised if they are the same since the node is\n"
28606 << "repeated!!!\n";
28607 throw OomphLibError(
28608 error_message.str(),
28609 "RefineableTriangleMesh::construct_new_halo_node_helper()",
28610 OOMPH_EXCEPTION_LOCATION);
28611 } // if (found_node_pt[0] != new_nod_pt)
28612
28613 } // if (is_node_on_shared_boundary == 1)
28614 else
28615 {
28616 // Take the first instance of the node in case that it was
28617 // found and is not on a shared boundary with the iproc
28618 // processor
28619 new_nod_pt = found_node_pt[0];
28620 }
28621
28622 } // if (found_node_in_other_shared_boundaries)
28623
28624 } // if (is_the_node_in_shared_boundaries_with_other_processors == 4)
28625
28626 // -----------------------------------------------------------------
28627 // Create the node or read the received info if the node is not on a
28628 // shared boundary with the iproc processor and if the node is not
28629 // part of the haloed elements with the iproc processor in the
28630 // current processors
28631 if (is_node_on_shared_boundary != 1 &&
28632 !on_haloed_element_with_iproc_processor)
28633 {
28634 // If the node is on a shared boundary with other processor we
28635 // need to read all the info. since the processor that sent the
28636 // info. did not know that the node is part of another shared
28637 // boundary
28638
28639 // If the node is not a shared boundary (with any processor), or
28640 // if this is the first time that the info. of the node is
28641 // received from any of the processors with which is has a shared
28642 // boundary, then we create the node
28643
28644 // Is the node a boundary node or should it be build as a boundary
28645 // node because it is on a shared boundary with other processors
28646 if (node_on_original_boundaries == 2 || build_node_as_boundary_node)
28647 {
28648 // Check if necessary to create the node, or if it has been
28649 // already found in shared boundaries with other processors or
28650 // in the haloed elements with of other processors with the
28651 // iproc processor
28652 if (!found_node_in_other_shared_boundaries ||
28653 !found_on_haloed_element_with_other_processor)
28654 {
28655 // Construct a boundary node
28656 if (time_stepper_pt != 0)
28657 {
28658 new_nod_pt =
28659 new_el_pt->construct_boundary_node(node_index, time_stepper_pt);
28660 }
28661 else
28662 {
28663 new_nod_pt = new_el_pt->construct_boundary_node(node_index);
28664 }
28665
28666 } // if (!found_node_in_other_shared_boundaries ||
28667 // !found_on_haloed_element_with_other_processor)
28668 else
28669 {
28670 // If the node was found then assign the node to the element
28671 new_el_pt->node_pt(node_index) = new_nod_pt;
28672 } // else if (!found_node_in_other_shared_boundaries ||
28673 // !found_on_haloed_element_with_other_processor)
28674
28675 // Associate the node to the given boundaries
28676 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
28677 {
28678 add_boundary_node(original_boundaries_node_is_on[i], new_nod_pt);
28679 // Establish the boundary coordinates for the node
28680 Vector<double> zeta(1);
28681 zeta[0] = zeta_coordinates[i];
28682 new_nod_pt->set_coordinates_on_boundary(
28683 original_boundaries_node_is_on[i], zeta);
28684 }
28685
28686 } // if (node is on an original boundary)
28687 else
28688 {
28689 // Check if necessary to create the node, or if it has been
28690 // already found in shared boundaries with other processors or
28691 // in the haloed elements with of other processors with the
28692 // iproc processor
28693 if (!found_node_in_other_shared_boundaries ||
28694 !found_on_haloed_element_with_other_processor)
28695 {
28696 // Construct an ordinary (non-boundary) node
28697 if (time_stepper_pt != 0)
28698 {
28699 new_nod_pt = new_el_pt->construct_node(node_index, time_stepper_pt);
28700 }
28701 else
28702 {
28703 new_nod_pt = new_el_pt->construct_node(node_index);
28704 }
28705 } // if (!found_node_in_other_shared_boundaries ||
28706 // !found_on_haloed_element_with_other_processor)
28707 else
28708 {
28709 // If the node was found then assign the node to the element
28710 new_el_pt->node_pt(node_index) = new_nod_pt;
28711 } // else // if (!found_node_in_other_shared_boundaries ||
28712 // !found_on_haloed_element_with_other_processor)
28713
28714 } // else (the node is not a boundary node)
28715
28716 // ... and gather all its information
28717
28718 // If the node was found or not in other shared boundaries, this
28719 // is the first time the node is received from this processor
28720 // (iproc), therefore it is added to the vector of nodes received
28721 // from this processor (iproc)
28722 new_nodes_on_domain.push_back(new_nod_pt);
28723
28724 // Check if necessary to state all the info. to the node if it has
28725 // been already found in shared boundaries with other processors
28726 // or in the haloed elements with of other processors with the
28727 // iproc processor
28728 if (!found_node_in_other_shared_boundaries ||
28729 !found_on_haloed_element_with_other_processor)
28730 {
28731 // Add the node to the general node storage
28732 this->add_node_pt(new_nod_pt);
28733 } // if (!found_node_in_other_shared_boundaries ||
28734 // !found_on_haloed_element_with_other_processor)
28735
28736 // Is the new constructed node Algebraic?
28737 AlgebraicNode* new_alg_nod_pt = dynamic_cast<AlgebraicNode*>(new_nod_pt);
28738
28739 // If it is algebraic, its node update functions will
28740 // not yet have been set up properly
28741 if (new_alg_nod_pt != 0)
28742 {
28743 // The AlgebraicMesh is the external mesh
28744 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(this);
28745
28746 /// The first entry of All_alg_nodal_info contains
28747 /// the default node update id
28748 /// e.g. for the quarter circle there are
28749 /// "Upper_left_box", "Lower right box" etc...
28750#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28752 << " Alg node update id "
28754 << std::endl;
28755#endif
28756
28757 unsigned update_id =
28759
28760 Vector<double> ref_value;
28761
28762 // The size of this vector is in the next entry
28763 // of All_alg_nodal_info
28764#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28766 << " Alg node # of ref values "
28768 << std::endl;
28769#endif
28770 unsigned n_ref_val =
28772
28773 // The reference values themselves are in
28774 // All_alg_ref_value
28775 ref_value.resize(n_ref_val);
28776 for (unsigned i_ref = 0; i_ref < n_ref_val; i_ref++)
28777 {
28778 ref_value[i_ref] =
28780 }
28781
28782 Vector<GeomObject*> geom_object_pt;
28783 /// again we need the size of this vector as it varies
28784 /// between meshes; we also need some indication
28785 /// as to which geometric object should be used...
28786
28787 // The size of this vector is in the next entry
28788 // of All_alg_nodal_info
28789#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28791 << " Alg node # of geom objects "
28793 << std::endl;
28794#endif
28795 unsigned n_geom_obj =
28797
28798 // The remaining indices are in the rest of
28799 // All_alg_nodal_info
28800 geom_object_pt.resize(n_geom_obj);
28801 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
28802 {
28803#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28805 << " Alg node: geom object index "
28807 << std::endl;
28808#endif
28809 unsigned geom_index =
28811 // This index indicates which of the AlgebraicMesh's
28812 // stored geometric objects should be used
28813 // (0 is a null pointer; everything else should have
28814 // been filled in by the specific Mesh). If it
28815 // hasn't been filled in then the update_node_update
28816 // call should fix it
28817 geom_object_pt[i_geom] = alg_mesh_pt->geom_object_list_pt(geom_index);
28818 }
28819
28820 // Check if necessary to state all the info. to the node if it
28821 // has been already found in shared boundaries with other
28822 // processors or in the haloed elements with of other processors
28823 // with the iproc processor
28824 if (!found_node_in_other_shared_boundaries ||
28825 !found_on_haloed_element_with_other_processor)
28826 {
28827 /// For the received update_id, ref_value, geom_object
28828 /// call add_node_update_info
28829 new_alg_nod_pt->add_node_update_info(
28830 update_id, alg_mesh_pt, geom_object_pt, ref_value);
28831
28832 /// Now call update_node_update
28833 alg_mesh_pt->update_node_update(new_alg_nod_pt);
28834
28835 } // if (!found_node_in_other_shared_boundaries ||
28836 // !found_on_haloed_element_with_other_processor)
28837
28838 } // if (new_alg_nod_pt!=0)
28839
28840 // Check if necessary to state all the info. to the node if it has
28841 // been already found in shared boundaries with other processors
28842 // or in the haloed elements with of other processors with the
28843 // iproc processor
28844 if (!found_node_in_other_shared_boundaries ||
28845 !found_on_haloed_element_with_other_processor)
28846 {
28847 // Is the node a MacroElementNodeUpdateNode?
28848 MacroElementNodeUpdateNode* macro_nod_pt =
28849 dynamic_cast<MacroElementNodeUpdateNode*>(new_nod_pt);
28850
28851 if (macro_nod_pt != 0)
28852 {
28853 // Need to call set_node_update_info; this requires
28854 // a Vector<GeomObject*> (taken from the mesh)
28855 Vector<GeomObject*> geom_object_vector_pt;
28856
28857 // Access the required geom objects from the
28858 // MacroElementNodeUpdateMesh
28859 MacroElementNodeUpdateMesh* macro_mesh_pt =
28860 dynamic_cast<MacroElementNodeUpdateMesh*>(this);
28861 geom_object_vector_pt = macro_mesh_pt->geom_object_vector_pt();
28862
28863 // Get local coordinate of node in new element
28864 Vector<double> s_in_macro_node_update_element;
28865 new_el_pt->local_coordinate_of_node(node_index,
28866 s_in_macro_node_update_element);
28867
28868 // Set node update info for this node
28869 macro_nod_pt->set_node_update_info(
28870 new_el_pt, s_in_macro_node_update_element, geom_object_vector_pt);
28871 }
28872
28873 } // if (!found_node_in_other_shared_boundaries ||
28874 // !found_on_haloed_element_with_other_processor)
28875
28876 // If there are additional values, resize the node
28877 unsigned n_new_val = new_nod_pt->nvalue();
28878
28879 // Check if necessary to state all the info. to the node if it has
28880 // been already found in shared boundaries with other processors
28881 // or in the haloed elements with of other processors with the
28882 // iproc processor
28883 if (!found_node_in_other_shared_boundaries ||
28884 !found_on_haloed_element_with_other_processor)
28885 {
28886 if (n_val > n_new_val)
28887 {
28888 // If it has been necessary to resize then it may be becuse
28889 // the node is on a FSI boundary, if that is the case we need
28890 // to set a map for these external values
28891
28892 // Cast to a boundary node
28893 BoundaryNodeBase* bnod_pt =
28894 dynamic_cast<BoundaryNodeBase*>(new_nod_pt);
28895
28896 // Create storage, if it doesn't already exist, for the map
28897 // that will contain the position of the first entry of
28898 // this face element's additional values,
28900 {
28902 new std::map<unsigned, unsigned>;
28903 }
28904
28905 // Get pointer to the map
28906 std::map<unsigned, unsigned>* map_pt =
28908
28909 // The id of the face to which this node belong in the bulk
28910 // element
28911 const unsigned id_face = 0;
28912 // We only resize the node values Vector if we haven't done it yet
28913 std::map<unsigned, unsigned>::const_iterator p =
28914 map_pt->find(id_face);
28915
28916 // If this node hasn't been resized for current id
28917 if (p == map_pt->end())
28918 {
28919 // assign the face element id and the position of the
28920 // first entry to the boundary node
28921 (*map_pt)[id_face] = n_new_val;
28922
28923 // resize the node vector of values
28924 new_nod_pt->resize(n_val);
28925 }
28926
28927 } // if (n_val>n_new_val)
28928
28929 } // if (!found_node_in_other_shared_boundaries ||
28930 // !found_on_haloed_element_with_other_processor)
28931
28932 // Is the new node a SolidNode?
28933 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(new_nod_pt);
28934 if (solid_nod_pt != 0)
28935 {
28936 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
28937 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
28938 {
28939 for (unsigned t = 0; t < n_prev; t++)
28940 {
28941 double read_data =
28943
28944 // Check if necessary to state all the info. to the node if
28945 // it has been already found in shared boundaries with other
28946 // processors or in the haloed elements with of other
28947 // processors with the iproc processor
28948 if (!found_node_in_other_shared_boundaries ||
28949 !found_on_haloed_element_with_other_processor)
28950 {
28951 solid_nod_pt->variable_position_pt()->set_value(
28952 t, i_val, read_data);
28953 } // if (!found_node_in_other_shared_boundaries ||
28954 // !found_on_haloed_element_with_other_processor)
28955 }
28956 }
28957
28958#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28960 << " Number of values solid node: "
28962 << std::endl;
28963#endif
28964 const unsigned nvalues_solid_node =
28966 Vector<double> values_solid_node(nvalues_solid_node);
28967 for (unsigned i = 0; i < nvalues_solid_node; i++)
28968 {
28969 values_solid_node[i] =
28971 }
28972
28973 // Check if necessary to state all the info. to the node if it
28974 // has been already found in shared boundaries with other
28975 // processors or in the haloed elements with of other processors
28976 // with the iproc processor
28977 if (!found_node_in_other_shared_boundaries ||
28978 !found_on_haloed_element_with_other_processor)
28979 {
28980 unsigned index = 0;
28981 solid_nod_pt->read_values_from_vector(values_solid_node, index);
28982 } // if (!found_node_in_other_shared_boundaries ||
28983 // !found_on_haloed_element_with_other_processor)
28984 }
28985
28986 // Get copied history values
28987 // unsigned n_val=new_nod_pt->nvalue();
28988 for (unsigned i_val = 0; i_val < n_val; i_val++)
28989 {
28990 for (unsigned t = 0; t < n_prev; t++)
28991 {
28992 double read_data =
28994
28995 // Check if necessary to state all the info. to the node if it
28996 // has been already found in shared boundaries with other
28997 // processors or in the haloed elements with of other
28998 // processors with the iproc processor
28999 if (!found_node_in_other_shared_boundaries ||
29000 !found_on_haloed_element_with_other_processor)
29001 {
29002 new_nod_pt->set_value(t, i_val, read_data);
29003 } // if (!found_node_in_other_shared_boundaries ||
29004 // !found_on_haloed_element_with_other_processor)
29005 }
29006 }
29007
29008 // Get copied history values for positions
29009 unsigned n_dim = new_nod_pt->ndim();
29010 for (unsigned idim = 0; idim < n_dim; idim++)
29011 {
29012 for (unsigned t = 0; t < n_prev; t++)
29013 {
29014 double read_data =
29016
29017 // Check if necessary to state all the info. to the node if it
29018 // has been already found in shared boundaries with other
29019 // processors or in the haloed elements with of other
29020 // processors with the iproc processor
29021 if (!found_node_in_other_shared_boundaries ||
29022 !found_on_haloed_element_with_other_processor)
29023 {
29024 // Copy to coordinate
29025 new_nod_pt->x(t, idim) = read_data;
29026 // DEBP(new_nod_pt->x(t,idim));
29027 } // if (!found_node_in_other_shared_boundaries ||
29028 // !found_on_haloed_element_with_other_processor)
29029 }
29030 }
29031
29032 } // if (is_node_on_shared_boundary != 1)
29033
29034 // If the node was not found in other shared boundaries (possibly
29035 // because it is the first time the node has been sent) then copy
29036 // the node to the shared boundaries where it should be, use the
29037 // special container for this cases
29038 if (n_shd_bnd_with_other_procs_have_node > 0 && // The node is on
29039 // shared
29040 // boundaries with
29041 // other processors
29042 !found_node_in_other_shared_boundaries) // The node has not
29043 // been previously
29044 // set as with
29045 // shared with
29046 // other processors
29047 // (first time)
29048 {
29049 // Update the node pointer in all the references of the node
29050 this->update_other_proc_shd_bnd_node_helper(new_nod_pt,
29051 other_proc_shd_bnd_node_pt,
29052 other_processor_1,
29053 other_processor_2,
29054 other_shared_boundaries,
29055 other_indexes,
29056 global_node_names,
29057 node_name_to_global_index,
29058 global_shared_node_pt);
29059
29060 } // if (!found_node_in_other_shared_boundaries)
29061 }
29062
29063#endif // #ifdef OOMPH_HAS_MPI
29064
29065 //======================================================================
29066 /// Get the nodes on the boundary (b), these are stored in the
29067 /// segment they belong (also used by the load balance method to
29068 /// re-set the number of segments per boundary after load balance has
29069 /// taken place)
29070 //======================================================================
29071 template<class ELEMENT>
29073 const unsigned& b, Vector<Vector<Node*>>& tmp_segment_nodes)
29074 {
29075 // Clear the data structure were to return the nodes
29076 tmp_segment_nodes.clear();
29077
29078 // Temporary storage for face elements
29079 Vector<FiniteElement*> face_el_pt;
29080
29081 // Temporary storage for number of elements adjacent to the boundary
29082 unsigned nel = 0;
29083
29084 // Temporary storage for elements adjacent to the boundary that have
29085 // a common edge (related with internal boundaries)
29086 unsigned n_repeated_ele = 0;
29087
29088 // Get the number of regions
29089 const unsigned n_regions = this->nregion();
29090
29091 // Temporary storage for already visited pair of nodes (edges)
29092 Vector<std::pair<Node*, Node*>> done_nodes_pt;
29093
29094 // Are there more than one region?
29095 if (n_regions > 1)
29096 {
29097 for (unsigned rr = 0; rr < n_regions; rr++)
29098 {
29099 const unsigned region_id =
29100 static_cast<unsigned>(this->Region_attribute[rr]);
29101
29102 // Loop over all elements on boundaries in region rr
29103 const unsigned nel_in_region =
29104 this->nboundary_element_in_region(b, region_id);
29105
29106 // Number of repeated element in region
29107 unsigned nel_repeated_in_region = 0;
29108
29109 // Only bother to do anything else, if there are elements
29110 // associated with the boundary and the current region
29111 if (nel_in_region > 0)
29112 {
29113 // Flag that activates when a repeated face element is found,
29114 // possibly because we are dealing with an internal boundary
29115 bool repeated = false;
29116
29117 // Loop over the bulk elements adjacent to boundary b
29118 for (unsigned e = 0; e < nel_in_region; e++)
29119 {
29120 // Get pointer to the bulk element that is adjacent to boundary b
29121 FiniteElement* bulk_elem_pt =
29122 this->boundary_element_in_region_pt(b, region_id, e);
29123
29124#ifdef OOMPH_HAS_MPI
29125 // In a distributed mesh only work with nonhalo elements
29126 if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
29127 {
29128 // Increase the number of repeated elements
29129 n_repeated_ele++;
29130 // Go for the next element
29131 continue;
29132 }
29133#endif
29134
29135 // Find the index of the face of element e along boundary b
29136 int face_index =
29137 this->face_index_at_boundary_in_region(b, region_id, e);
29138
29139 // Before adding the new element we need to be sure that the
29140 // edge that this element represents has not been already
29141 // added
29142 FiniteElement* tmp_ele_pt =
29143 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
29144
29145 // Number of nodes in the face element
29146 const unsigned n_nodes = tmp_ele_pt->nnode();
29147
29148 std::pair<Node*, Node*> tmp_pair = std::make_pair(
29149 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
29150
29151 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
29152 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
29153
29154 // Search for repeated nodes
29155 unsigned n_done_nodes = done_nodes_pt.size();
29156 for (unsigned l = 0; l < n_done_nodes; l++)
29157 {
29158 if (tmp_pair == done_nodes_pt[l] ||
29159 tmp_pair_inverse == done_nodes_pt[l])
29160 {
29161 nel_repeated_in_region++;
29162 repeated = true;
29163 break;
29164 }
29165
29166 } // for (l < n_done_nodes)
29167
29168 // Create new face element?
29169 if (!repeated)
29170 {
29171 // Add the pair of nodes (edge) to the node dones
29172 done_nodes_pt.push_back(tmp_pair);
29173 // Add the face element to the storage
29174 face_el_pt.push_back(tmp_ele_pt);
29175 }
29176 else
29177 {
29178 // Clean up
29179 delete tmp_ele_pt;
29180 tmp_ele_pt = 0;
29181 }
29182
29183 // Re-start
29184 repeated = false;
29185
29186 } // for (e < nel_in_region)
29187
29188 // Add on the number of elements in the boundary with the
29189 // current region
29190 nel += nel_in_region;
29191
29192 // Add on the number of repeated elements
29193 n_repeated_ele += nel_repeated_in_region;
29194
29195 } // if (nel_in_region > 0)
29196
29197 } // for (rr < n_regions)
29198
29199 } // if (n_regions > 1)
29200 // Otherwise it's just the normal boundary functions
29201 else
29202 {
29203 // Assign the number of boundary elements
29204 nel = this->nboundary_element(b);
29205
29206 // Only bother to do anything else, if there are elements
29207 if (nel > 0)
29208 {
29209 // Flag that activates when a repeated face element is found,
29210 // possibly because we are dealing with an internal boundary
29211 bool repeated = false;
29212
29213 // Loop over the bulk elements adjacent to boundary b
29214 for (unsigned e = 0; e < nel; e++)
29215 {
29216 // Get pointer to the bulk element that is adjacent to boundary b
29217 FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
29218
29219#ifdef OOMPH_HAS_MPI
29220 // In a distributed mesh only work with nonhalo elements
29221 if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
29222 {
29223 // Increase the number of repeated elements
29224 n_repeated_ele++;
29225 // Go for the next element
29226 continue;
29227 }
29228#endif
29229
29230 // Find the index of the face of element e along boundary b
29231 int face_index = this->face_index_at_boundary(b, e);
29232
29233 // Before adding the new element we need to be sure that the
29234 // edge that this element represent has not been already added
29235 FiniteElement* tmp_ele_pt =
29236 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
29237
29238 // Number of nodes in the face element
29239 const unsigned n_nodes = tmp_ele_pt->nnode();
29240
29241 std::pair<Node*, Node*> tmp_pair = std::make_pair(
29242 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
29243
29244 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
29245 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
29246
29247 // Search for repeated nodes
29248 unsigned n_done_nodes = done_nodes_pt.size();
29249 for (unsigned l = 0; l < n_done_nodes; l++)
29250 {
29251 if (tmp_pair == done_nodes_pt[l] ||
29252 tmp_pair_inverse == done_nodes_pt[l])
29253 {
29254 n_repeated_ele++;
29255 repeated = true;
29256 break;
29257 }
29258
29259 } // for (l < n_done_nodes)
29260
29261 // Create new face element
29262 if (!repeated)
29263 {
29264 // Add the pair of nodes (edge) to the node dones
29265 done_nodes_pt.push_back(tmp_pair);
29266 // Add the face element to the storage
29267 face_el_pt.push_back(tmp_ele_pt);
29268 }
29269 else
29270 {
29271 // Free the repeated bulk element!!
29272 delete tmp_ele_pt;
29273 tmp_ele_pt = 0;
29274 }
29275
29276 // Re-start
29277 repeated = false;
29278
29279 } // for (e < nel)
29280
29281 } // if (nel > 0)
29282
29283 } // else if (n_regions > 1)
29284
29285 // Substract the repeated elements
29286 nel -= n_repeated_ele;
29287
29288#ifdef PARANOID
29289 if (nel != face_el_pt.size())
29290 {
29291 std::ostringstream error_message;
29292 error_message
29293 << "The independet counting of face elements (" << nel << ") for "
29294 << "boundary (" << b << ") is different\n"
29295 << "from the real number of face elements in the container ("
29296 << face_el_pt.size() << ")\n";
29297 throw OomphLibError(
29298 error_message.str(),
29299 "RefineableTriangleMesh::get_boundary_segment_nodes_helper()",
29300 OOMPH_EXCEPTION_LOCATION);
29301 }
29302#endif
29303
29304 // Only bother to do anything else, if there are elements
29305 if (nel > 0)
29306 {
29307 // Assign the number of nonhalo face elements
29308 const unsigned nnon_halo_face_elements = nel;
29309
29310 // The vector of list to store the "segments" that compound the
29311 // boundary (segments may appear only in a distributed mesh)
29312 Vector<std::list<FiniteElement*>> segment_sorted_ele_pt;
29313
29314 // Number of already sorted face elements (only nonhalo face
29315 // elements for a distributed mesh)
29316 unsigned nsorted_face_elements = 0;
29317
29318 // Keep track of who's done (in a distributed mesh this apply to
29319 // nonhalo only)
29320 std::map<FiniteElement*, bool> done_ele;
29321
29322 // Keep track of which element is inverted (in distributed mesh
29323 // the elements may be inverted with respect to the segment they
29324 // belong)
29325 std::map<FiniteElement*, bool> is_inverted;
29326
29327 // Iterate until all possible segments have been created. In a non
29328 // distributed mesh there is only one segment which defines the
29329 // complete boundary
29330 while (nsorted_face_elements < nnon_halo_face_elements)
29331 {
29332 // The ordered list of face elements (in a distributed mesh a
29333 // collection of continuous face elements define a segment)
29334 std::list<FiniteElement*> sorted_el_pt;
29335
29336#ifdef PARANOID
29337 // Select an initial element for the segment
29338 bool found_initial_face_element = false;
29339#endif
29340
29341 FiniteElement* ele_face_pt = 0;
29342
29343 unsigned iface = 0;
29344#ifdef OOMPH_HAS_MPI
29345 if (this->is_mesh_distributed())
29346 {
29347 for (iface = 0; iface < nel; iface++)
29348 {
29349 ele_face_pt = face_el_pt[iface];
29350 // If not done then take it as initial face element
29351 if (!done_ele[ele_face_pt])
29352 {
29353#ifdef PARANOID
29354 // Set the flag to indicate the initial element was
29355 // found
29356 found_initial_face_element = true;
29357#endif
29358 // Increase the number of sorted face elements
29359 nsorted_face_elements++;
29360 // Set the index to the next face element
29361 iface++;
29362 // Add the face element in the container
29363 sorted_el_pt.push_back(ele_face_pt);
29364 // Mark as done
29365 done_ele[ele_face_pt] = true;
29366 break;
29367 } // if (!done_el[ele_face_pt])
29368 } // for (iface < nel)
29369 } // if (this->is_mesh_distributed())
29370 else
29371 {
29372#endif // #ifdef OOMPH_HAS_MPI
29373
29374 // When the mesh is not distributed just take the first
29375 // element and put it in the ordered list
29376 ele_face_pt = face_el_pt[0];
29377#ifdef PARANOID
29378 // Set the flag to indicate the initial element was found
29379 found_initial_face_element = true;
29380#endif
29381 // Increase the number of sorted face elements
29382 nsorted_face_elements++;
29383 // Set the index to the next face element
29384 iface = 1;
29385 // Add the face element in the container
29386 sorted_el_pt.push_back(ele_face_pt);
29387 // Mark as done
29388 done_ele[ele_face_pt] = true;
29389#ifdef OOMPH_HAS_MPI
29390 } // else if (this->is_mesh_distributed())
29391#endif
29392
29393#ifdef PARANOID
29394 if (!found_initial_face_element)
29395 {
29396 std::ostringstream error_message;
29397 error_message << "Could not find an initial face element for the "
29398 "current segment\n";
29399 throw OomphLibError(
29400 error_message.str(),
29401 "RefineableTriangleMesh::get_boundary_segment_nodes_helper()",
29402 OOMPH_EXCEPTION_LOCATION);
29403 }
29404#endif
29405
29406 // Number of nodes in the face element
29407 const unsigned nnod = ele_face_pt->nnode();
29408
29409 // Left and rightmost nodes (the left and right nodes of the
29410 // current face element)
29411 Node* left_node_pt = ele_face_pt->node_pt(0);
29412 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
29413
29414 // Continue iterating if a new face element has been added to
29415 // the list
29416 bool face_element_added = false;
29417
29418 // While a new face element has been added to the set of sorted
29419 // face elements continue iterating
29420 do
29421 {
29422 // Start from the next face element since we have already
29423 // added the previous one as the initial face element (any
29424 // previous face element had to be added on previous
29425 // iterations)
29426 for (unsigned iiface = iface; iiface < nel; iiface++)
29427 {
29428 // Re-start flag
29429 face_element_added = false;
29430
29431 // Get the candidate element
29432 ele_face_pt = face_el_pt[iiface];
29433
29434 // Check that the candidate element has not been done and is
29435 // not a halo element
29436 if (!done_ele[ele_face_pt])
29437 {
29438 // Get the left and right nodes of the current element
29439 Node* local_left_node_pt = ele_face_pt->node_pt(0);
29440 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
29441
29442 // New element fits at the left of segment and is not inverted
29443 if (left_node_pt == local_right_node_pt)
29444 {
29445 left_node_pt = local_left_node_pt;
29446 sorted_el_pt.push_front(ele_face_pt);
29447 is_inverted[ele_face_pt] = false;
29448 face_element_added = true;
29449 }
29450 // New element fits at the left of segment and is inverted
29451 else if (left_node_pt == local_left_node_pt)
29452 {
29453 left_node_pt = local_right_node_pt;
29454 sorted_el_pt.push_front(ele_face_pt);
29455 is_inverted[ele_face_pt] = true;
29456 face_element_added = true;
29457 }
29458 // New element fits on the right of segment and is not inverted
29459 else if (right_node_pt == local_left_node_pt)
29460 {
29461 right_node_pt = local_right_node_pt;
29462 sorted_el_pt.push_back(ele_face_pt);
29463 is_inverted[ele_face_pt] = false;
29464 face_element_added = true;
29465 }
29466 // New element fits on the right of segment and is inverted
29467 else if (right_node_pt == local_right_node_pt)
29468 {
29469 right_node_pt = local_left_node_pt;
29470 sorted_el_pt.push_back(ele_face_pt);
29471 is_inverted[ele_face_pt] = true;
29472 face_element_added = true;
29473 }
29474
29475 if (face_element_added)
29476 {
29477 // Mark the face element as done
29478 done_ele[ele_face_pt] = true;
29479 nsorted_face_elements++;
29480 break;
29481 }
29482
29483 } // if (!done_el[ele_face_pt])
29484
29485 } // for (iiface<nnon_halo_face_element)
29486
29487 } while (face_element_added &&
29488 (nsorted_face_elements < nnon_halo_face_elements));
29489
29490 // Store the created segment in the vector of segments
29491 segment_sorted_ele_pt.push_back(sorted_el_pt);
29492
29493 } // while(nsorted_face_elements < nnon_halo_face_elements);
29494
29495 // The number of boundary segments in this processor
29496 const unsigned nsegments = segment_sorted_ele_pt.size();
29497
29498#ifdef PARANOID
29499 if (nnon_halo_face_elements > 0 && nsegments == 0)
29500 {
29501 std::ostringstream error_message;
29502 error_message
29503 << "The number of segments is zero, but the number of nonhalo\n"
29504 << "elements is: (" << nnon_halo_face_elements << ")\n";
29505 throw OomphLibError(
29506 error_message.str(),
29507 "RefineableTriangleMesh::get_boundary_segment_nodes_helper()",
29508 OOMPH_EXCEPTION_LOCATION);
29509 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
29510#endif
29511
29512 // Go through all the segments, visit each face element in order
29513 // and get the nodes based that represent the boundary segment
29514
29515 // Resize the container to store the nodes with the required
29516 // number of segments
29517 tmp_segment_nodes.resize(nsegments);
29518
29519 for (unsigned is = 0; is < nsegments; is++)
29520 {
29521#ifdef PARANOID
29522 if (segment_sorted_ele_pt[is].size() == 0)
29523 {
29524 std::ostringstream error_message;
29525 error_message << "The (" << is << ")-th segment has no elements\n";
29526 throw OomphLibError(
29527 error_message.str(),
29528 "RefineableTriangleMesh::get_boundary_segment_nodes_helper()",
29529 OOMPH_EXCEPTION_LOCATION);
29530 } // if (segment_sorted_ele_pt[is].size() == 0)
29531#endif
29532
29533 // Get access to the first element on the segment
29534 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
29535
29536 // Number of nodes
29537 const unsigned nnod = first_ele_pt->nnode();
29538
29539 // Get the first node of the current segment
29540 Node* first_node_pt = first_ele_pt->node_pt(0);
29541 if (is_inverted[first_ele_pt])
29542 {
29543 first_node_pt = first_ele_pt->node_pt(nnod - 1);
29544 }
29545
29546 // Add the node to the corresponding segment
29547 tmp_segment_nodes[is].push_back(first_node_pt);
29548
29549 // Now loop over face elements in order to get the nodes
29550 for (std::list<FiniteElement*>::iterator it =
29551 segment_sorted_ele_pt[is].begin();
29552 it != segment_sorted_ele_pt[is].end();
29553 it++)
29554 {
29555 // Get element
29556 FiniteElement* ele_pt = *it;
29557
29558 // The last node pointer
29559 Node* last_node_pt = 0;
29560
29561 // Get the last node
29562 if (!is_inverted[ele_pt])
29563 {
29564 last_node_pt = ele_pt->node_pt(nnod - 1);
29565 }
29566 else
29567 {
29568 last_node_pt = ele_pt->node_pt(0);
29569 }
29570
29571 // Add the node to the corresponding segment
29572 tmp_segment_nodes[is].push_back(last_node_pt);
29573
29574 } // iterator over the elements in the segment
29575
29576 } // for (is < nsegments)
29577
29578 } // for (if (nel > 0))
29579
29580 // Free memory allocation
29581 for (unsigned e = 0; e < nel; e++)
29582 {
29583 delete face_el_pt[e];
29584 face_el_pt[e] = 0;
29585 } // for (e < nel)
29586 }
29587
29588 //======================================================================
29589 /// Adapt problem based on specified elemental error estimates
29590 /// This function implement serial and parallel mesh adaptation, the
29591 /// sections for parallel mesh adaptation are clearly identified by
29592 /// checking whether the mesh is distributed or not
29593 //======================================================================
29594 template<class ELEMENT>
29596 {
29597 double t_start_overall = TimingHelpers::timer();
29598
29599 // ==============================================================
29600 // BEGIN: Compute target areas
29601 // ==============================================================
29602
29603 // Get refinement targets
29604 Vector<double> target_area(elem_error.size());
29605 double min_angle = compute_area_target(elem_error, target_area);
29606
29607 // Post-process to allow only quantised target areas
29608 // in an attempt to more closely mimick the structured
29609 // case and limit the diffusion of small elements.
29610 bool quantised_areas = true;
29611 if (quantised_areas)
29612 {
29613 unsigned n = target_area.size();
29614 double total_area = 0;
29615 // If the mesh is distributed then we need to get the contribution
29616 // of all processors to compute the total areas
29617 // ------------------------------------------
29618 // DISTRIBUTED MESH: BEGIN
29619 // ------------------------------------------
29620#ifdef OOMPH_HAS_MPI
29621 if (this->is_mesh_distributed())
29622 {
29623 // When working in parallel we get the total area from the sum
29624 // of the the sub-areas of all the meshes
29625 double sub_area = 0.0;
29626
29627 // Only add the area of nonhalo elements
29628 for (unsigned e = 0; e < n; e++)
29629 {
29630 // Get the pointer to the element
29631 FiniteElement* ele_pt = this->finite_element_pt(e);
29632 if (!ele_pt->is_halo())
29633 {
29634 sub_area += ele_pt->size();
29635 }
29636 } // for (e<n)
29637
29638 // Get the communicator of the mesh
29639 OomphCommunicator* comm_pt = this->communicator_pt();
29640
29641 // Get the total area
29642 MPI_Allreduce(
29643 &sub_area, &total_area, 1, MPI_DOUBLE, MPI_SUM, comm_pt->mpi_comm());
29644 }
29645 else
29646 {
29647 for (unsigned e = 0; e < n; e++)
29648 {
29649 total_area += this->finite_element_pt(e)->size();
29650 }
29651 }
29652 // ------------------------------------------
29653 // DISTRIBUTED MESH: END
29654 // ------------------------------------------
29655#else // #ifdef OOMPH_HAS_MPI
29656 for (unsigned e = 0; e < n; e++)
29657 {
29658 total_area += this->finite_element_pt(e)->size();
29659 }
29660#endif // #ifdef OOMPH_HAS_MPI
29661
29662 for (unsigned e = 0; e < n; e++)
29663 {
29664 unsigned level =
29665 unsigned(ceil(log(target_area[e] / total_area) / log(1.0 / 3.0))) - 1;
29666 double new_target_area = total_area * pow(1.0 / 3.0, int(level));
29667 target_area[e] = new_target_area;
29668 }
29669 }
29670
29671 // std::ofstream tmp;
29672 // tmp.open((Global_string_for_annotation::
29673 // String[0]+"overall_target_areas"+
29674 // StringConversion::to_string(Global_unsigned::Number)+".dat").c_str());
29675
29676 // Get maximum target area
29677 unsigned n = target_area.size();
29678 double max_area = 0.0;
29679 double min_area = DBL_MAX;
29680 for (unsigned e = 0; e < n; e++)
29681 {
29682 if (target_area[e] > max_area) max_area = target_area[e];
29683 if (target_area[e] < min_area) min_area = target_area[e];
29684
29685 // tmp << (finite_element_pt(e)->node_pt(0)->x(0)+
29686 // finite_element_pt(e)->node_pt(1)->x(0)+
29687 // finite_element_pt(e)->node_pt(2)->x(0))/3.0 << " "
29688 // << (finite_element_pt(e)->node_pt(0)->x(1)+
29689 // finite_element_pt(e)->node_pt(1)->x(1)+
29690 // finite_element_pt(e)->node_pt(2)->x(1))/3.0 << " "
29691 // << target_area[e] << " "
29692 // << finite_element_pt(e)->size() << " "
29693 // << elem_error[e] << " " << std::endl;
29694 }
29695
29696 // tmp.close();
29697
29698 oomph_info << "Maximum target area: " << max_area << std::endl;
29699 oomph_info << "Minimum target area: " << min_area << std::endl;
29700 oomph_info << "Number of elements to be refined: " << this->Nrefined
29701 << std::endl;
29702 oomph_info << "Number of elements to be unrefined: " << this->Nunrefined
29703 << std::endl;
29704 oomph_info << "Min. angle: " << min_angle << std::endl;
29705
29706 double orig_max_area, orig_min_area;
29707 this->max_and_min_element_size(orig_max_area, orig_min_area);
29708 oomph_info << "Max./min. element size in original mesh: " << orig_max_area
29709 << " " << orig_min_area << std::endl;
29710
29711 // ==============================================================
29712 // END: Compute target areas
29713 // ==============================================================
29714
29715 // Check if boundaries need to be updated (regardless of
29716 // requirements of bulk error estimator) but don't do anything!
29717 bool check_only = true;
29718 bool outer_boundary_update_necessary = false;
29719 bool inner_boundary_update_necessary = false;
29720 bool inner_open_boundary_update_necessary = false;
29721
29722 // Get the number of outer boundaries and check if they require
29723 // update
29724 const unsigned nouter = this->Outer_boundary_pt.size();
29725
29726 if (this->is_automatic_creation_of_vertices_on_boundaries_allowed())
29727 {
29728 // loop over the outer boundaries
29729 for (unsigned i_outer = 0; i_outer < nouter; i_outer++)
29730 {
29731 outer_boundary_update_necessary = this->update_polygon_using_face_mesh(
29732 this->Outer_boundary_pt[i_outer], check_only);
29733 // Break the loop if at least one needs updating
29734 if (outer_boundary_update_necessary) break;
29735 }
29736
29737 // Do not waste time if we already know that it is necessary an update
29738 // on the boundary representation
29739 if (!outer_boundary_update_necessary)
29740 {
29741 // Check if we need to generate a new 1D mesh representation of
29742 // the inner hole boundaries
29743 const unsigned nhole = this->Internal_polygon_pt.size();
29744 Vector<Vector<double>> internal_point_coord(nhole);
29745 inner_boundary_update_necessary =
29746 this->surface_remesh_for_inner_hole_boundaries(internal_point_coord,
29747 check_only);
29748
29749 // If there was not necessary a change even on the internal closed
29750 // curve then finally check for the open curves as well
29751 if (!inner_boundary_update_necessary)
29752 {
29753 const unsigned n_open_polyline = this->Internal_open_curve_pt.size();
29754 // loop over the open polylines
29755 for (unsigned i = 0; i < n_open_polyline; i++)
29756 {
29757 inner_open_boundary_update_necessary =
29758 this->update_open_curve_using_face_mesh(
29759 this->Internal_open_curve_pt[i], check_only);
29760 // If at least one needs modification then break the for loop
29761 if (inner_open_boundary_update_necessary) break;
29762 }
29763 }
29764 }
29765 }
29766
29767 // Flag to indicate whether we need to adapt or not (for parallel
29768 // mesh adaptation only)
29769 int adapt_all = 0;
29770 // ------------------------------------------
29771 // DISTRIBUTED MESH: BEGIN
29772 // ------------------------------------------
29773#ifdef OOMPH_HAS_MPI
29774 // When working in distributed meshes we need to ensure that all the
29775 // processors take part on the adaptation process. If at least one
29776 // of the processors requires adaptation then all processor take
29777 // part on the adaptation process.
29778 int adapt_this_processor = 0;
29779 if (this->is_mesh_distributed())
29780 {
29781 // Do this processor requires adaptation?
29782 if ((Nrefined > 0) || (Nunrefined > max_keep_unrefined()) ||
29783 (min_angle < min_permitted_angle()) ||
29784 (outer_boundary_update_necessary) ||
29785 (inner_boundary_update_necessary) ||
29786 (inner_open_boundary_update_necessary))
29787 {
29788 adapt_this_processor = 1;
29789 }
29790
29791 // Get the communicator of the mesh
29792 OomphCommunicator* comm_pt = this->communicator_pt();
29793
29794 // Verify if at least one processor needs mesh adaptation
29795 MPI_Allreduce(&adapt_this_processor,
29796 &adapt_all,
29797 1,
29798 MPI_INT,
29799 MPI_SUM,
29800 comm_pt->mpi_comm());
29801 }
29802#endif
29803 // ------------------------------------------
29804 // DISTRIBUTED MESH: END
29805 // ------------------------------------------
29806
29807 // Should we bother to adapt?
29808 if ((Nrefined > 0) || (Nunrefined > max_keep_unrefined()) ||
29809 (min_angle < min_permitted_angle()) ||
29810 (outer_boundary_update_necessary) ||
29811 (inner_boundary_update_necessary) ||
29812 (inner_open_boundary_update_necessary) || (adapt_all))
29813 {
29814 if (!((Nrefined > 0) || (Nunrefined > max_keep_unrefined())))
29815 {
29816 if ((outer_boundary_update_necessary) ||
29817 (inner_boundary_update_necessary) ||
29818 (inner_open_boundary_update_necessary))
29819 {
29821 << "Mesh regeneration triggered by inaccurate interface/surface\n"
29822 << "representation; setting Nrefined to number of elements.\n"
29823 << "outer_boundary_update_necessary : "
29824 << outer_boundary_update_necessary << "\n"
29825 << "inner_boundary_update_necessary : "
29826 << inner_boundary_update_necessary << "\n"
29827 << "inner_open_boundary_update_necessary: "
29828 << inner_open_boundary_update_necessary << "\n";
29829 Nrefined = nelement();
29830 }
29831 else
29832 {
29833 oomph_info << "Mesh regeneration triggered by min angle criterion;\n"
29834 << "setting Nrefined to number of elements.\n";
29835 Nrefined = nelement();
29836 }
29837 }
29838
29839 // ------------------------------------------
29840 // DISTRIBUTED MESH: BEGIN
29841 // ------------------------------------------
29842#ifdef OOMPH_HAS_MPI
29843 else if (this->is_mesh_distributed() && adapt_this_processor == 0 &&
29844 adapt_all > 0)
29845 {
29846 oomph_info << "Mesh regeneration triggered by (" << adapt_all
29847 << ") processor(s) "
29848 << "that require(s)\n adaptation\n";
29849 }
29850#endif
29851 // ------------------------------------------
29852 // DISTRIBUTED MESH: END
29853 // ------------------------------------------
29854
29855 // ==============================================================
29856 // BEGIN: Updating of boundaries representation (unrefinement and
29857 // refinement of polylines)
29858 // ==============================================================
29859
29860 // Add the initial and final vertices of the polylines that
29861 // present connections to a list of non-delete-able vertices. The
29862 // vertices where the connections are performed cannot be deleted
29863 add_vertices_for_non_deletion();
29864
29865 // ------------------------------------------
29866 // DISTRIBUTED MESH: BEGIN
29867 // ------------------------------------------
29868#ifdef OOMPH_HAS_MPI
29869 // Synchronise connections for shared boundaries among
29870 // processors. This is required since one of the processor may noy
29871 // know that some of its shared boundaries have connections, thus
29872 // the vertices receiving the connections cannot be deleted
29873 if (this->is_mesh_distributed())
29874 {
29875 synchronize_shared_boundary_connections();
29876 }
29877#endif // #ifdef OOMPH_HAS_MPI
29878 // ------------------------------------------
29879 // DISTRIBUTED MESH: END
29880 // ------------------------------------------
29881
29882 // Are we allowing automatic insertion of vertices on boundaries?
29883 // If YES then Triangle automatically insert points along
29884 // boundaries, if NOT, then points are inserted along the
29885 // boundaries based on the target areas of boundary elements. When
29886 // the mesh is distributed the automatic insertion of vertices by
29887 // Triangle along the boundaries is not allowed
29888 if (this->is_automatic_creation_of_vertices_on_boundaries_allowed())
29889 {
29890 // Generate a new 1D mesh representation of the inner hole boundaries
29891 unsigned nhole = this->Internal_polygon_pt.size();
29892 Vector<Vector<double>> internal_point_coord(nhole);
29893 this->surface_remesh_for_inner_hole_boundaries(internal_point_coord);
29894
29895 // Update the representation of the outer boundary
29896 for (unsigned i_outer = 0; i_outer < nouter; i_outer++)
29897 {
29898 this->update_polygon_using_face_mesh(
29899 this->Outer_boundary_pt[i_outer]);
29900 }
29901
29902 // After updating outer and internal closed boundaries it is also
29903 // necessary to update internal boundaries.
29904 unsigned n_open_polyline = this->Internal_open_curve_pt.size();
29905 for (unsigned i = 0; i < n_open_polyline; i++)
29906 {
29907 this->update_open_curve_using_face_mesh(
29908 this->Internal_open_curve_pt[i]);
29909 }
29910 }
29911 else
29912 {
29913 // Update the representation of the internal boundaries using
29914 // the element's target area
29915
29916 // Get the number of interal polygons
29917 const unsigned ninternal = this->Internal_polygon_pt.size();
29918 for (unsigned i_internal = 0; i_internal < ninternal; i_internal++)
29919 {
29920 this->update_polygon_using_elements_area(
29921 this->Internal_polygon_pt[i_internal], target_area);
29922 }
29923
29924 // Update the representation of the outer boundaries using the
29925 // element's target area
29926 for (unsigned i_outer = 0; i_outer < nouter; i_outer++)
29927 {
29928 this->update_polygon_using_elements_area(
29929 this->Outer_boundary_pt[i_outer], target_area);
29930 }
29931
29932 // Update the representation of the internal open boundaries
29933 // using the element's target areas
29934 const unsigned n_open_polyline = this->Internal_open_curve_pt.size();
29935 for (unsigned i = 0; i < n_open_polyline; i++)
29936 {
29937 this->update_open_curve_using_elements_area(
29938 this->Internal_open_curve_pt[i], target_area);
29939 }
29940
29941 // ------------------------------------------
29942 // DISTRIBUTED MESH: BEGIN
29943 // ------------------------------------------
29944
29945 // When working with a distributed mesh we require to update the
29946 // boundary representation of the shared boundaries, this is
29947 // based on the target areas of the elements adjaced to the
29948 // shared boundaries
29949#ifdef OOMPH_HAS_MPI
29950 // Update shared boundaries if the mesh is distributed
29951 if (this->is_mesh_distributed())
29952 {
29953 // Get the rank of the current processor
29954 const unsigned my_rank = this->communicator_pt()->my_rank();
29955
29956 // Get the number of shared curves
29957 const unsigned n_curves = this->nshared_boundary_curves(my_rank);
29958 // Loop over the shared curves in the current processor
29959 for (unsigned nc = 0; nc < n_curves; nc++)
29960 {
29961 // Update the shared polyline
29962 this->update_shared_curve_using_elements_area(
29963 this->Shared_boundary_polyline_pt[my_rank][nc], // shared_curve,
29964 target_area);
29965 }
29966
29967 } // if (this->is_mesh_distributed())
29968#endif
29969
29970 // ------------------------------------------
29971 // DISTRIBUTED MESH: END
29972 // ------------------------------------------
29973
29974 } // else if
29975 // (this->is_automatic_creation_of_vertices_on_boundaries_allowed())
29976
29977 // ==============================================================
29978 // END: Updating of boundaries representation (unrefinement and
29979 // refinement of polylines)
29980 // ==============================================================
29981
29982 // ==============================================================
29983 // BEGIN: Reset boundary coordinates for boundaries with no
29984 // associated GeomObject
29985 // ==============================================================
29986
29987 // If there is not a geometric object associated with the boundary
29988 // then reset the boundary coordinates so that the lengths are
29989 // consistent in the new mesh and the old mesh.
29990 const unsigned n_boundary = this->nboundary();
29991
29992 const double t_start_first_stage_segments_connectivity =
29994
29995 // ------------------------------------------
29996 // DISTRIBUTED MESH: BEGIN
29997 // ------------------------------------------
29998#ifdef OOMPH_HAS_MPI
29999 // Clear storage for assignment of initial zeta values for
30000 // boundaries
30001 if (this->is_mesh_distributed())
30002 {
30003 this->Assigned_segments_initial_zeta_values.clear();
30004 }
30005#endif // #ifdef OOMPH_HAS_MPI
30006 // ------------------------------------------
30007 // DISTRIBUTED MESH: END
30008 // ------------------------------------------
30009
30010 // Loop over the boundaries to assign boundary coordinates
30011 for (unsigned b = 0; b < n_boundary; ++b)
30012 {
30013 // ------------------------------------------
30014 // DISTRIBUTED MESH: BEGIN
30015 // ------------------------------------------
30016#ifdef OOMPH_HAS_MPI
30017 if (this->is_mesh_distributed())
30018 {
30019 // In a distributed mesh, the boundaries may have been split
30020 // across processors during the distribution process, thus we
30021 // need to compute the connectivity among the segments of the
30022 // boundary to correctly assign its boundary coordinates
30023 this->compute_boundary_segments_connectivity_and_initial_zeta_values(
30024 b);
30025 }
30026#endif
30027 // ------------------------------------------
30028 // DISTRIBUTED MESH: END
30029 // ------------------------------------------
30030
30031 // Does the boundary has an associated GeomObject
30032 if (this->boundary_geom_object_pt(b) == 0)
30033 {
30034 this->template setup_boundary_coordinates<ELEMENT>(b);
30035 }
30036
30037 // ------------------------------------------
30038 // DISTRIBUTED MESH: BEGIN
30039 // ------------------------------------------
30040#ifdef OOMPH_HAS_MPI
30041 if (this->is_mesh_distributed())
30042 {
30043 // Synchronise boundary coordinates for internal open curves,
30044 // also establish the boundary coordinates for the nodes on
30045 // the corners of elements not on the boundary
30046 this->synchronize_boundary_coordinates(b);
30047 }
30048#endif
30049 // ------------------------------------------
30050 // DISTRIBUTED MESH: END
30051 // ------------------------------------------
30052
30053 } // for (b<n_boundary)
30054
30055 const double t_total_first_stage_segments_connectivity =
30056 TimingHelpers::timer() - t_start_first_stage_segments_connectivity;
30057
30058 // ==============================================================
30059 // END: Reset boundary coordinates for boundaries with no
30060 // associated GeomObject
30061 // ==============================================================
30062
30063 // ------------------------------------------
30064 // DISTRIBUTED MESH: BEGIN
30065 // ------------------------------------------
30066#ifdef OOMPH_HAS_MPI
30067 // ==============================================================
30068 // BEGIN: Create the new representation of the domain by joining
30069 // the original boundaries and the shared boundaries.
30070 // ==============================================================
30071
30072 // Storage for the new temporary polygons "closed" by the shared
30073 // boundaries
30074 Vector<TriangleMeshPolygon*> tmp_outer_polygons_pt;
30075
30076 // Storage for the new temporary open curves, could be the
30077 // original open curves or "chunks" of the original open curves
30078 // not overlapped by shared boundaries
30079 Vector<TriangleMeshOpenCurve*> tmp_open_curves_pt;
30080
30081 if (this->is_mesh_distributed())
30082 {
30083 // Create the new polygons and open curves with help of the
30084 // original polylines and shared polylines
30085 this->create_distributed_domain_representation(tmp_outer_polygons_pt,
30086 tmp_open_curves_pt);
30087
30088 // Create the connections of the temporary domain representations
30089 this->create_temporary_boundary_connections(tmp_outer_polygons_pt,
30090 tmp_open_curves_pt);
30091 }
30092 // ==============================================================
30093 // END: Create the new representation of the domain by joining
30094 // the original boundaries and the shared boundaries.
30095 // ==============================================================
30096#endif
30097 // ------------------------------------------
30098 // DISTRIBUTED MESH: END
30099 // ------------------------------------------
30100
30101 // Re-establish polylines' connections. The boundary
30102 // representation has changed (new polylines), therefore we need
30103 // to update the connection information
30104 Vector<TriangleMeshPolyLine*> resume_initial_connection_polyline_pt;
30105 Vector<TriangleMeshPolyLine*> resume_final_connection_polyline_pt;
30106 restore_boundary_connections(resume_initial_connection_polyline_pt,
30107 resume_final_connection_polyline_pt);
30108
30109 // Update the region information by setting the coordinates from the
30110 // centroid of the first element in each region (which should allow
30111 // automatic updates when the regions deform)
30112 {
30113 unsigned n_region = this->nregion();
30114 if (n_region > 1)
30115 {
30116 for (std::map<unsigned, Vector<double>>::iterator it =
30117 this->Regions_coordinates.begin();
30118 it != this->Regions_coordinates.end();
30119 ++it)
30120 {
30121 // Storage for the approximate centroid
30122 Vector<double> centroid(2, 0.0);
30123
30124 // Get the region id
30125 unsigned region_id = it->first;
30126
30127 // Report information
30128 oomph_info << "Region " << region_id << ": " << it->second[0] << " "
30129 << it->second[1] << " ";
30130
30131 // Check that there is at least one element in the region
30132 unsigned n_region_element = this->nregion_element(region_id);
30133 if (n_region_element > 0)
30134 {
30135 // Cache pointer to the first element
30136 FiniteElement* const elem_pt =
30137 this->region_element_pt(region_id, 0);
30138
30139 // Loop over the corners of the triangle and average
30140 for (unsigned n = 0; n < 3; n++)
30141 {
30142 Node* const nod_pt = elem_pt->node_pt(n);
30143 for (unsigned i = 0; i < 2; i++)
30144 {
30145 centroid[i] += nod_pt->x(i);
30146 }
30147 }
30148 for (unsigned i = 0; i < 2; i++)
30149 {
30150 centroid[i] /= 3;
30151 }
30152 // Now we have the centroid set it
30153 it->second = centroid;
30154
30155 oomph_info << " , " << it->second[0] << " " << it->second[1]
30156 << std::endl;
30157 } // end of case when there is at least one element
30158
30159 } // loop over regions coordinates
30160
30161 } // if(n_region > 1)
30162
30163 } // Updating region info.
30164
30165 // ==============================================================
30166 // BEGIN: Create background mesh
30167 // ==============================================================
30168
30169 // Are we dealing with a solid mesh?
30170 SolidMesh* solid_mesh_pt = dynamic_cast<SolidMesh*>(this);
30171
30172 // Build temporary uniform background mesh
30173 //----------------------------------------
30174 // with area set by maximum required area
30175 //---------------------------------------
30176 RefineableTriangleMesh<ELEMENT>* tmp_new_mesh_pt = 0;
30177
30178 // The storage for the new temporary boundaries representation to
30179 // create the background mesh
30180 Vector<TriangleMeshClosedCurve*> closed_curve_pt;
30182 Vector<TriangleMeshOpenCurve*> open_curves_pt;
30183
30184#ifdef OOMPH_HAS_MPI
30185 if (!this->is_mesh_distributed())
30186#endif
30187 {
30188 // Copy the outer boundaries
30189 closed_curve_pt.resize(nouter);
30190 for (unsigned i = 0; i < nouter; i++)
30191 {
30192 closed_curve_pt[i] = this->Outer_boundary_pt[i];
30193 }
30194
30195 // Copy the internal closed boundaries (may be holes)
30196 const unsigned n_holes = this->Internal_polygon_pt.size();
30197 hole_pt.resize(n_holes);
30198 for (unsigned i = 0; i < n_holes; i++)
30199 {
30200 hole_pt[i] = this->Internal_polygon_pt[i];
30201 }
30202
30203 // Copy the internal open curves
30204 const unsigned n_open_curves = this->Internal_open_curve_pt.size();
30205 open_curves_pt.resize(n_open_curves);
30206 for (unsigned i = 0; i < n_open_curves; i++)
30207 {
30208 open_curves_pt[i] = this->Internal_open_curve_pt[i];
30209 }
30210 }
30211 // ------------------------------------------
30212 // DISTRIBUTED MESH: BEGIN
30213 // ------------------------------------------
30214#ifdef OOMPH_HAS_MPI
30215 else
30216 {
30217 // Copy the new representation of the outer/internal closed
30218 // boundaries
30219 const unsigned n_tmp_outer = tmp_outer_polygons_pt.size();
30220 closed_curve_pt.resize(n_tmp_outer);
30221 for (unsigned i = 0; i < n_tmp_outer; i++)
30222 {
30223 closed_curve_pt[i] = tmp_outer_polygons_pt[i];
30224 }
30225
30226 // Copy the new representation of the internal open curves
30227 const unsigned n_open_curves = tmp_open_curves_pt.size();
30228 open_curves_pt.resize(n_open_curves);
30229 for (unsigned i = 0; i < n_open_curves; i++)
30230 {
30231 open_curves_pt[i] = tmp_open_curves_pt[i];
30232 }
30233 }
30234#endif
30235 // ------------------------------------------
30236 // DISTRIBUTED MESH: END
30237 // ------------------------------------------
30238
30239 // ----------------------------------------------------------------
30240 // Gather all the information and use the TriangleMeshParameters
30241 // object which help us on the manage of all TriangleMesh object's
30242 // information
30243
30244 // Create the TriangleMeshParameters objects with the outer boundary
30245 // as the only one parameter
30246 TriangleMeshParameters triangle_mesh_parameters(closed_curve_pt);
30247
30248 // Pass information about the holes
30249 triangle_mesh_parameters.internal_closed_curve_pt() = hole_pt;
30250
30251 // Pass information about the internal open boundaries
30252 triangle_mesh_parameters.internal_open_curves_pt() = open_curves_pt;
30253
30254 // Set the element area
30255 triangle_mesh_parameters.element_area() = max_area;
30256
30257 // Pass information about the extra holes (not defined with closed
30258 // boundaries)
30259 triangle_mesh_parameters.extra_holes_coordinates() =
30260 this->Extra_holes_coordinates;
30261
30262 // Pass information about regions
30263 triangle_mesh_parameters.regions_coordinates() =
30264 this->Regions_coordinates;
30265
30266 // Pass information about the using of regions
30267 if (this->Use_attributes)
30268 {
30269 triangle_mesh_parameters.enable_use_attributes();
30270 }
30271
30272 // Pass information about allowing the creation of new points
30273 if (!this->is_automatic_creation_of_vertices_on_boundaries_allowed())
30274 {
30275 triangle_mesh_parameters
30277 }
30278
30279 // When the mesh is distributed we need to create a distributed
30280 // background mesh
30281#ifdef OOMPH_HAS_MPI
30282 if (this->is_mesh_distributed())
30283 {
30284 // Mark the mesh to be created as distributed by passing a
30285 // pointer to the communicator
30286 triangle_mesh_parameters.set_communicator_pt(this->communicator_pt());
30287 }
30288#endif
30289
30290 // ----------------------------------------------------------
30291 // Build the background mesh using Triangle
30292 // ----------------------------------------------------------
30293 const double t_start_building_background_mesh = TimingHelpers::timer();
30294
30295 if (solid_mesh_pt != 0)
30296 {
30297 tmp_new_mesh_pt = new RefineableSolidTriangleMesh<ELEMENT>(
30298 triangle_mesh_parameters, this->Time_stepper_pt);
30299 }
30300 else
30301 {
30302 tmp_new_mesh_pt = new RefineableTriangleMesh<ELEMENT>(
30303 triangle_mesh_parameters, this->Time_stepper_pt);
30304 }
30305
30306 if (Print_timings_level_adaptation > 2)
30307 {
30308 oomph_info << "CPU for building background mesh: "
30309 << TimingHelpers::timer() - t_start_building_background_mesh
30310 << std::endl;
30311 }
30312
30313 // Pass the info. regarding the maximum and minimum element size
30314 // from the old mesh to the background mesh
30315 const double this_max_element_size = this->max_element_size();
30316 const double this_min_element_size = this->min_element_size();
30317 tmp_new_mesh_pt->max_element_size() = this_max_element_size;
30318 tmp_new_mesh_pt->min_element_size() = this_min_element_size;
30319
30320 // ... also copy the minimum permitted angle
30321 const double this_min_permitted_angle = this->min_permitted_angle();
30322 tmp_new_mesh_pt->min_permitted_angle() = this_min_permitted_angle;
30323
30324 // ------------------------------------------
30325 // DISTRIBUTED MESH: BEGIN
30326 // ------------------------------------------
30327#ifdef OOMPH_HAS_MPI
30328 // If the mesh is distributed we need to pass and set the
30329 // information of internal boundaries overlaped by shared
30330 // boundaries
30331 if (this->is_mesh_distributed())
30332 {
30333 // Check if necessary to fill boundary elements for those
30334 // internal boundaries that overlap shared boundaries
30335 if (this->nshared_boundary_overlaps_internal_boundary() > 0)
30336 {
30337 // Copy the data structures that indicates which shared
30338 // boundaries are part of an internal boundary
30340 this->shared_boundary_overlaps_internal_boundary();
30341
30342 // Copy the data structure that indicates which are the shared
30343 // boundaries in each processor
30344 tmp_new_mesh_pt->shared_boundaries_ids() =
30345 this->shared_boundaries_ids();
30346
30347 // Fill the structures for the boundary elements and face indexes
30348 // of the boundary elements
30349 tmp_new_mesh_pt
30351
30352 } // if (this->nshared_boundary_overlaps_internal_boundary() > 0)
30353
30354 } // if (this->is_mesh_distributed())
30355#endif // #ifdef OOMPH_HAS_MPI
30356 // ------------------------------------------
30357 // DISTRIBUTED MESH: END
30358 // ------------------------------------------
30359
30360 // Snap to curvilinear boundaries (some code duplication as this
30361 // is repeated below but helper function would take so many
30362 // arguments that it's nearly as messy...
30363
30364 // Pass the boundary geometric objects to the new mesh
30365 tmp_new_mesh_pt->boundary_geom_object_pt() =
30366 this->boundary_geom_object_pt();
30367
30368 // Reset the boundary coordinates if there is
30369 // a geometric object associated with the boundary
30370 tmp_new_mesh_pt->boundary_coordinate_limits() =
30371 this->boundary_coordinate_limits();
30372
30373 const double t_start_second_stage_segments_connectivity =
30375
30376 for (unsigned b = 0; b < n_boundary; b++)
30377 {
30378 // ------------------------------------------
30379 // DISTRIBUTED MESH: BEGIN
30380 // ------------------------------------------
30381#ifdef OOMPH_HAS_MPI
30382 if (this->is_mesh_distributed())
30383 {
30384 // Identify the segments of the new mesh with the ones of the
30385 // original mesh
30386 tmp_new_mesh_pt
30388 this);
30389 }
30390#endif
30391 // ------------------------------------------
30392 // DISTRIBUTED MESH: END
30393 // ------------------------------------------
30394
30395 // Setup boundary coordinates for boundaries with GeomObject
30396 // associated
30397 if (tmp_new_mesh_pt->boundary_geom_object_pt(b) != 0)
30398 {
30399 tmp_new_mesh_pt->template setup_boundary_coordinates<ELEMENT>(b);
30400 }
30401 }
30402
30403 const double t_total_second_stage_segments_connectivity =
30404 TimingHelpers::timer() - t_start_second_stage_segments_connectivity;
30405
30406 const double t_start_snap_nodes_bg_mesh = TimingHelpers::timer();
30407 // Move the nodes on the new boundary onto the old curvilinear
30408 // boundary. If the boundary is straight this will do precisely
30409 // nothing but will be somewhat inefficient
30410 for (unsigned b = 0; b < n_boundary; b++)
30411 {
30412 this->snap_nodes_onto_boundary(tmp_new_mesh_pt, b);
30413 }
30414
30415 const double t_total_snap_nodes_bg_mesh =
30416 TimingHelpers::timer() - t_start_snap_nodes_bg_mesh;
30417
30418 if (Print_timings_level_adaptation > 2)
30419 {
30420 oomph_info << "CPU for snapping nodes onto boundaries "
30421 << "(background mesh): " << t_total_snap_nodes_bg_mesh
30422 << std::endl;
30423 }
30424
30425 // Update mesh further?
30426 if (Mesh_update_fct_pt != 0)
30427 {
30428 Mesh_update_fct_pt(tmp_new_mesh_pt);
30429 }
30430
30431 // If we have a continuation problem
30432 // any problem in which the timestepper is a "generalisedtimestepper",
30433 // which will have been set by the problem, then ensure
30434 // all data in the new mesh has the appropriate timestepper
30435 /*if(dynamic_cast<GeneralisedTimeStepper*>(this->Time_stepper_pt))
30436 {
30437 tmp_new_mesh_pt->set_nodal_and_elemental_time_stepper(
30438 this->Time_stepper_pt);
30439 tmp_new_mesh_pt->set_mesh_level_time_stepper(this->Time_stepper_pt);
30440 }*/
30441
30442
30443 // tmp_new_mesh_pt->output("mesh_nodes_snapped_0.dat");
30444 // this->output("existing_mesh.dat");
30445
30446 // ==============================================================
30447 // END: Create background mesh
30448 // ==============================================================
30449
30450 // ==============================================================
30451 // BEGIN: Transferring of target areas and creation of new mesh
30452 // ==============================================================
30453
30454 // Get the TriangulateIO object associated with that mesh
30455 TriangulateIO tmp_new_triangulateio =
30456 tmp_new_mesh_pt->triangulateio_representation();
30457 RefineableTriangleMesh<ELEMENT>* new_mesh_pt = 0;
30458
30459 // If the mesh is a solid mesh then do the mapping based on the
30460 // Eulerian coordinates
30461 bool use_eulerian_coords = false;
30462 if (solid_mesh_pt != 0)
30463 {
30464 use_eulerian_coords = true;
30465 }
30466
30467
30468#ifdef OOMPH_HAS_CGAL
30469
30470 // Make cgal-based bin
30471 CGALSamplePointContainerParameters cgal_params(this);
30472 if (use_eulerian_coords)
30473 {
30475 }
30476 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(&cgal_params);
30477
30478#else
30479
30480 // Make nonrefineable bin
30482 if (use_eulerian_coords)
30483 {
30485 }
30486 Vector<unsigned> bin_dim(2);
30487 bin_dim[0] = Nbin_x_for_area_transfer;
30488 bin_dim[1] = Nbin_y_for_area_transfer;
30489 params.dimensions_of_bin_array() = bin_dim;
30490 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(&params);
30491
30492#endif
30493
30494 // Set up a map from pointer to element to its number
30495 // in the mesh
30496 std::map<GeneralisedElement*, unsigned> element_number;
30497 unsigned nelem = this->nelement();
30498 for (unsigned e = 0; e < nelem; e++)
30499 {
30500 element_number[this->element_pt(e)] = e;
30501 }
30502
30503#ifndef OOMPH_HAS_CGAL
30504
30505 // Create a vector to store the min target area of each bin (at
30506 // this stage the number of bins should not be that large, so it
30507 // should be safe to build a vector for the total number of bins)
30508 Vector<double> bin_min_target_area;
30509
30510 // Get pointer to sample point container
30511 NonRefineableBinArray* bin_array_pt =
30512 dynamic_cast<NonRefineableBinArray*>(
30513 mesh_geom_obj_pt->sample_point_container_pt());
30514 if (bin_array_pt == 0)
30515 {
30516 throw OomphLibError(
30517 "Sample point container has to be NonRefineableBinArray",
30518 OOMPH_CURRENT_FUNCTION,
30519 OOMPH_EXCEPTION_LOCATION);
30520 }
30521
30522 {
30523 unsigned n_bin = 0;
30524 unsigned max_n_entry = 0;
30525 unsigned min_n_entry = UINT_MAX;
30526 unsigned tot_n_entry = 0;
30527 unsigned n_empty = 0;
30528 bin_array_pt->get_fill_stats(
30529 n_bin, max_n_entry, min_n_entry, tot_n_entry, n_empty);
30530
30531 oomph_info << "Before bin diffusion:"
30532 << " nbin:(" << n_bin << ")"
30533 << " nempty:(" << n_empty << ")"
30534 << " min:(" << min_n_entry << ")"
30535 << " max:(" << max_n_entry << ")"
30536 << " average entries:("
30537 << double(tot_n_entry) / double(n_bin) << ")" << std::endl;
30538 }
30539
30540 // Fill bin by diffusion
30541 double t0_bin_diff = TimingHelpers::timer();
30542 oomph_info << "Going into diffusion bit...\n";
30543 bin_array_pt->fill_bin_by_diffusion();
30544 oomph_info << "Back from diffusion bit...\n";
30545 oomph_info << "Time for bin diffusion: "
30546 << TimingHelpers::timer() - t0_bin_diff << std::endl;
30547
30548 // Do some stats
30549 {
30550 unsigned n_bin = 0;
30551 unsigned max_n_entry = 0;
30552 unsigned min_n_entry = UINT_MAX;
30553 unsigned tot_n_entry = 0;
30554 unsigned n_empty = 0;
30555 bin_array_pt->get_fill_stats(
30556 n_bin, max_n_entry, min_n_entry, tot_n_entry, n_empty);
30557
30558 oomph_info << "After bin diffusion:"
30559 << " nbin:(" << n_bin << ")"
30560 << " nempty:(" << n_empty << ")"
30561 << " min:(" << min_n_entry << ")"
30562 << " max:(" << max_n_entry << ")"
30563 << " average entries:("
30564 << double(tot_n_entry) / double(n_bin) << ")" << std::endl;
30565 }
30566
30567
30568 // For each bin, compute the minimum of the target areas in the bin
30569
30570 // Timing for map
30571 double t_total_map = 0.0;
30572
30573 // Counter for map
30574 unsigned counter_map = 0;
30575
30576 // Get access to the bins (we need access to the content of the
30577 // bins to compute the minimum of the target areas of the elements
30578 // in each bin)
30579 const std::map<unsigned,
30581 bins_pt = bin_array_pt->get_all_bins_content();
30582
30583 // Get the number of bins
30584 const unsigned n_bin = bins_pt->size();
30585
30586 // Create a vector to store the min target area of each bin (at
30587 // this stage the number of bins should not be that large, so it
30588 // should be safe to build a vector for the total number of bins)
30589 bin_min_target_area.resize(n_bin);
30590 for (unsigned u = 0; u < n_bin; u++)
30591 {
30592 bin_min_target_area[u] = 0.0;
30593 }
30594 // loop over the bins, get their elements and compute the minimum
30595 // target area of all of them
30596 typedef std::map<
30597 unsigned,
30599 for (IT it = bins_pt->begin(); it != bins_pt->end(); it++)
30600 {
30601 // The bin number
30602 unsigned ib = (*it).first;
30603
30604 // Get the number of elements in the bin
30605 const unsigned n_ele_bin = (*it).second.size();
30606
30607 // loop over the elements in the bin
30608 for (unsigned ee = 0; ee < n_ele_bin; ee++)
30609 {
30610 // Get ee-th element (in currrent mesh) in ib-th bin
30611 GeneralisedElement* ele_pt = (*it).second[ee].first;
30612 double t_map = TimingHelpers::timer();
30613 const unsigned ele_number = element_number[ele_pt];
30614 t_total_map += TimingHelpers::timer() - t_map;
30615
30616 // Increase the number of calls to map
30617 counter_map++;
30618
30619 // Go for smallest target area of any element in this bin to
30620 // force "one level" of refinement (the one-level-ness is
30621 // enforced below by limiting the actual reduction in area
30622 if (bin_min_target_area[ib] != 0)
30623 {
30624 bin_min_target_area[ib] =
30625 std::min(bin_min_target_area[ib], target_area[ele_number]);
30626 }
30627 else
30628 {
30629 bin_min_target_area[ib] = target_area[ele_number];
30630 }
30631
30632 } // for (ee<n_ele_bin)
30633
30634 } // for (it!=bins.end())
30635
30636 oomph_info << "CPU for map[counter=" << counter_map
30637 << "]: " << t_total_map << std::endl;
30638
30639
30640 // Optional output for debugging (keep it around!)
30641 const bool output_bins = false;
30642 if (output_bins)
30643 {
30644 unsigned length = bin_min_target_area.size();
30645 for (unsigned u = 0; u < length; u++)
30646 {
30647 oomph_info << "Bin n" << u
30648 << ",target area: " << bin_min_target_area[u] << std::endl;
30649 }
30650 }
30651
30652#endif
30653
30654
30655 // Now start iterating to refine mesh recursively
30656 //-----------------------------------------------
30657 bool done = false;
30658 unsigned iter = 0;
30659#ifdef OOMPH_HAS_MPI
30660 // The number of elements that require (un)refinement
30661 unsigned n_ele_need_refinement = 0;
30662#endif
30663
30664 // The timing for the third stage of segments connectivity
30665 double t_total_third_stage_segments_connectivity = 0.0;
30666
30667 // The timing for the transfering target areas
30668 double t_total_transfer_target_areas = 0.0;
30669
30670 // The timing for the copying of target areas
30671 double t_total_limit_target_areas = 0.0;
30672
30673 // The timing to create the new mesh
30674 double t_total_create_new_adapted_mesh = 0.0;
30675
30676 // The timing for the snapping of the nodes on the new meshes
30677 double t_total_snap_nodes = 0.0;
30678
30679 // The timing to check whether other processors need to adapt
30680 double t_total_wait_other_processors = 0.0;
30681 double t_iter = TimingHelpers::timer();
30682 while (!done)
30683 {
30684 // Accept by default but overwrite if things go wrong below
30685 done = true;
30686
30687 double t_start_transfer_target_areas = TimingHelpers::timer();
30688 double t0_loop_int_pts = TimingHelpers::timer();
30689
30690 // Loop over elements in new (tmp) mesh and visit all
30691 // its integration points. Check where it's located in the bin
30692 // structure of the current mesh and pass the target area
30693 // to the new element
30694 nelem = tmp_new_mesh_pt->nelement();
30695
30696 // Store the target areas for elements in the temporary
30697 // TriangulateIO mesh
30698 Vector<double> new_transferred_target_area(nelem, 0.0);
30699 for (unsigned e = 0; e < nelem; e++)
30700 { // start loop el
30701 ELEMENT* el_pt =
30702 dynamic_cast<ELEMENT*>(tmp_new_mesh_pt->element_pt(e));
30703 unsigned nint = el_pt->integral_pt()->nweight();
30704 for (unsigned ipt = 0; ipt < nint; ipt++)
30705 {
30706 // Get the coordinate of current point
30707 Vector<double> s(2);
30708 for (unsigned i = 0; i < 2; i++)
30709 {
30710 s[i] = el_pt->integral_pt()->knot(ipt, i);
30711 }
30712
30713 Vector<double> x(2);
30714 el_pt->interpolated_x(s, x);
30715
30716#if OOMPH_HAS_CGAL
30717
30718 // Try the five nearest sample points for Newton search
30719 // then just settle on the nearest one
30720 GeomObject* geom_obj_pt = 0;
30721 unsigned max_sample_points =
30722 Max_sample_points_for_limited_locate_zeta_during_target_area_transfer;
30723 dynamic_cast<CGALSamplePointContainer*>(
30724 mesh_geom_obj_pt->sample_point_container_pt())
30725 ->limited_locate_zeta(x, max_sample_points, geom_obj_pt, s);
30726#ifdef PARANOID
30727 if (geom_obj_pt == 0)
30728 {
30729 std::stringstream error_message;
30730 error_message << "Limited locate zeta failed for zeta = [ "
30731 << x[0] << " " << x[1] << " ]. Makes no sense!\n";
30732 throw OomphLibError(error_message.str(),
30733 OOMPH_CURRENT_FUNCTION,
30734 OOMPH_EXCEPTION_LOCATION);
30735 }
30736 else
30737 {
30738#endif
30739 FiniteElement* fe_pt = dynamic_cast<FiniteElement*>(geom_obj_pt);
30740#ifdef PARANOID
30741 if (fe_pt == 0)
30742 {
30743 std::stringstream error_message;
30744 error_message << "Cast to FE for GeomObject returned by "
30745 "limited locate zeta failed for zeta = [ "
30746 << x[0] << " " << x[1] << " ]. Makes no sense!\n";
30747 throw OomphLibError(error_message.str(),
30748 OOMPH_CURRENT_FUNCTION,
30749 OOMPH_EXCEPTION_LOCATION);
30750 }
30751 else
30752 {
30753#endif
30754 // What's the target area of the element that contains this
30755 // point
30756 double tg_area = target_area[element_number[fe_pt]];
30757
30758 // Go for smallest target area over all integration
30759 // points in new element
30760 // to force "one level" of refinement (the one-level-ness
30761 // is enforced below by limiting the actual reduction in
30762 // area
30763 if (new_transferred_target_area[e] != 0)
30764 {
30765 new_transferred_target_area[e] =
30766 std::min(new_transferred_target_area[e], tg_area);
30767 }
30768 else
30769 {
30770 new_transferred_target_area[e] = tg_area;
30771 }
30772#ifdef PARANOID
30773 }
30774 }
30775#endif
30776
30777#else
30778
30779 // Find the bin that contains that point and its contents
30780 int bin_number = 0;
30781 bin_array_pt->get_bin(x, bin_number);
30782
30783 // Did we find it?
30784 if (bin_number < 0)
30785 {
30786 // Not even within bin boundaries... odd
30787 std::stringstream error_message;
30788 error_message << "Very odd -- we're looking for a point[ " << x[0]
30789 << " " << x[1] << " ] that's not even \n"
30790 << "located within the bin boundaries.\n";
30791 throw OomphLibError(error_message.str(),
30792 "RefineableTriangleMesh::adapt()",
30793 OOMPH_EXCEPTION_LOCATION);
30794 } // if (bin_number<0)
30795 else
30796 {
30797 // Go for smallest target area of any element in this bin
30798 // to force "one level" of refinement (the one-level-ness
30799 // is enforced below by limiting the actual reduction in
30800 // area
30801 if (new_transferred_target_area[e] != 0)
30802 {
30803 new_transferred_target_area[e] =
30804 std::min(new_transferred_target_area[e],
30805 bin_min_target_area[bin_number]);
30806 }
30807 else
30808 {
30809 new_transferred_target_area[e] =
30810 bin_min_target_area[bin_number];
30811 }
30812 }
30813
30814#endif
30815
30816 } // for (ipt<nint)
30817
30818 } // for (e<nelem)
30819
30820
30821 // do some output (keep it alive!)
30822 const bool output_target_areas = false;
30823 if (output_target_areas)
30824 {
30825 unsigned length = new_transferred_target_area.size();
30826 for (unsigned u = 0; u < length; u++)
30827 {
30828 oomph_info << "Element" << u
30829 << ",target area: " << new_transferred_target_area[u]
30830 << std::endl;
30831 }
30832 }
30833 oomph_info << "Time for loop over integration points in new mesh: "
30834 << TimingHelpers::timer() - t0_loop_int_pts << std::endl;
30835
30836
30837 // {
30838 // tmp.open((Global_string_for_annotation::
30839 // String[0]+"binned_target_areas"+
30840 // StringConversion::to_string(Global_unsigned::Number)+".dat").c_str());
30841
30842 // Vector<Vector<std::pair<FiniteElement*,Vector<double> > > >
30843 // bin_content=
30844 // mesh_geom_obj_pt->bin_content();
30845 // unsigned nbin=bin_content.size();
30846 // for (unsigned b=0;b<nbin;b++)
30847 // {
30848 // unsigned nentry=bin_content[b].size();
30849 // for (unsigned entry=0;entry<nentry;entry++)
30850 // {
30851 // FiniteElement* el_pt=bin_content[b][entry].first;
30852 // GeneralisedElement* gen_el_pt=bin_content[b][entry].first;
30853 // Vector<double> s=bin_content[b][entry].second;
30854 // Vector<double> x(2);
30855 // el_pt->interpolated_x(s,x);
30856 // unsigned e_current=element_number[gen_el_pt];
30857 // tmp << x[0] << " " << x[1] << " "
30858 // << target_area[e_current] << " "
30859 // << el_pt->size() << " "
30860 // << std::endl;
30861 // }
30862 // }
30863 // tmp.close();
30864 // }
30865
30866 const double t_sub_total_transfer_target_areas =
30867 TimingHelpers::timer() - t_start_transfer_target_areas;
30868
30869 if (Print_timings_level_adaptation > 2)
30870 {
30871 // Get the number of elements in the old mesh (this)
30872 const unsigned n_element = this->nelement();
30873 // Get the number of elements in the background mesh
30874 const unsigned n_element_background = tmp_new_mesh_pt->nelement();
30875
30876 oomph_info << "CPU for transfer of target areas "
30877 << "[n_ele_old_mesh=" << n_element
30878 << ", n_ele_background_mesh=" << n_element_background
30879 << "] (iter " << iter
30880 << "): " << t_sub_total_transfer_target_areas << std::endl;
30881 }
30882
30883 // Add the timing for tranfer of target areas
30884 t_total_transfer_target_areas += t_sub_total_transfer_target_areas;
30885
30886 // // Output mesh
30887 // tmp_new_mesh_pt->output(("intermediate_mesh"+
30888 // StringConversion::to_string(iter)+".dat").c_str());
30889
30890 // tmp.open((Global_string_for_annotation::
30891 // String[0]+"target_areas_intermediate_mesh_iter"+
30892 // StringConversion::to_string(iter)+"_"+
30893 // StringConversion::to_string(Global_unsigned::Number)+".dat").c_str());
30894
30895 const double t_start_limit_target_areas = TimingHelpers::timer();
30896
30897 // Now copy into target area for temporary mesh but limit to
30898 // the equivalent of one sub-division per iteration
30899#ifdef OOMPH_HAS_MPI
30900 unsigned n_ele_need_refinement_iter = 0;
30901#endif
30902
30903
30904 // Don't delete! Keep these around for debugging
30905 // ofstream tmp_mesh_file;
30906 // tmp_mesh_file.open("tmp_mesh_file.dat");
30907 // tmp_new_mesh_pt->output(tmp_mesh_file);
30908 // tmp_mesh_file.close();
30909 // ofstream target_areas_file;
30910 // target_areas_file.open("target_areas_file.dat");
30911
30912 const unsigned nel_new = tmp_new_mesh_pt->nelement();
30913 Vector<double> new_target_area(nel_new);
30914 for (unsigned e = 0; e < nel_new; e++)
30915 {
30916 // The finite element
30917 FiniteElement* f_ele_pt = tmp_new_mesh_pt->finite_element_pt(e);
30918
30919 // Transferred target area
30920 const double new_area = new_transferred_target_area[e];
30921 if (new_area <= 0.0)
30922 {
30923 std::ostringstream error_stream;
30924 error_stream
30925 << "This shouldn't happen! Element whose centroid is at "
30926 << (f_ele_pt->node_pt(0)->x(0) + f_ele_pt->node_pt(1)->x(0) +
30927 f_ele_pt->node_pt(2)->x(0)) /
30928 3.0
30929 << " "
30930 << (f_ele_pt->node_pt(0)->x(1) + f_ele_pt->node_pt(1)->x(1) +
30931 f_ele_pt->node_pt(2)->x(1)) /
30932 3.0
30933 << " "
30934 << " has no target area assigned\n";
30935 throw OomphLibError(error_stream.str(),
30936 OOMPH_CURRENT_FUNCTION,
30937 OOMPH_EXCEPTION_LOCATION);
30938 }
30939 else
30940 {
30941 // Limit target area to the equivalent of uniform refinement
30942 // during this stage of the iteration
30943 new_target_area[e] = new_area;
30944 if (new_target_area[e] < f_ele_pt->size() / 3.0)
30945 {
30946 new_target_area[e] = f_ele_pt->size() / 3.0;
30947
30948 // We'll need to give it another go later
30949 done = false;
30950 }
30951
30952 // Don't delete! Keep around for debugging
30953 // target_areas_file
30954 // << (f_ele_pt->node_pt(0)->x(0)+
30955 // f_ele_pt->node_pt(1)->x(0)+
30956 // f_ele_pt->node_pt(2)->x(0))/3.0 << " "
30957 // << (f_ele_pt->node_pt(0)->x(1)+
30958 // f_ele_pt->node_pt(1)->x(1)+
30959 // f_ele_pt->node_pt(2)->x(1))/3.0 << " "
30960 // << new_area << " "
30961 // << new_target_area[e] << std::endl;
30962
30963
30964#ifdef OOMPH_HAS_MPI
30965 // Keep track of the elements that require (un)refinement
30966 n_ele_need_refinement_iter++;
30967#endif
30968
30969 } // else if (new_area <= 0.0)
30970
30971 } // for (e < nel_new)
30972
30973
30974 // Don't delete! Keep around for debugging
30975 // target_areas_file.close();
30976
30977 const double t_sub_total_limit_target_areas =
30978 TimingHelpers::timer() - t_start_limit_target_areas;
30979
30980 // Add the timing for copying target areas
30981 t_total_limit_target_areas += t_sub_total_limit_target_areas;
30982
30983 if (Print_timings_level_adaptation > 2)
30984 {
30985 // Get the number of elements in the old mesh (this)
30986 const unsigned n_element = this->nelement();
30987 // Get the number of elements in the background mesh
30988 const unsigned n_element_background = tmp_new_mesh_pt->nelement();
30989
30990 oomph_info << "CPU for limiting target areas "
30991 << "[n_ele_old_mesh=" << n_element
30992 << ", n_ele_background_mesh=" << n_element_background
30993 << "] (iter " << iter
30994 << "): " << t_sub_total_limit_target_areas << std::endl;
30995 }
30996
30997 if (done)
30998 {
31000 << "All area adjustments accommodated by max. permitted area"
31001 << " reduction \n";
31002 }
31003 else
31004 {
31005 oomph_info << "NOT all area adjustments accommodated by max. "
31006 << "permitted area reduction \n";
31007 }
31008
31009 // tmp.close();
31010 // pause("doced binned_target_areas.dat and intermediate mesh targets");
31011
31012 // Now create the new mesh from TriangulateIO structure
31013 //-----------------------------------------------------
31014 // associated with uniform background mesh and the
31015 //------------------------------------------------
31016 // associated target element sizes.
31017 //---------------------------------
31018
31019 const double t_start_create_new_adapted_mesh = TimingHelpers::timer();
31020
31021 // Solid mesh?
31022 if (solid_mesh_pt != 0)
31023 {
31024 new_mesh_pt = new RefineableSolidTriangleMesh<ELEMENT>(
31025 new_target_area,
31026 tmp_new_triangulateio,
31027 this->Time_stepper_pt,
31028 this->Use_attributes,
31029 this->Allow_automatic_creation_of_vertices_on_boundaries,
31030 this->communicator_pt());
31031 }
31032 // No solid mesh
31033 else
31034 {
31035 new_mesh_pt = new RefineableTriangleMesh<ELEMENT>(
31036 new_target_area,
31037 tmp_new_triangulateio,
31038 this->Time_stepper_pt,
31039 this->Use_attributes,
31040 this->Allow_automatic_creation_of_vertices_on_boundaries,
31041 this->communicator_pt());
31042 }
31043
31044 // Sub-total to create new adapted mesh
31045 const double t_sub_total_create_new_adapted_mesh =
31046 TimingHelpers::timer() - t_start_create_new_adapted_mesh;
31047
31048 // Add the time to the total snap nodes time
31049 t_total_create_new_adapted_mesh += t_sub_total_create_new_adapted_mesh;
31050
31051 if (Print_timings_level_adaptation > 2)
31052 {
31053 // Get the number of elements of the new adapted mesh
31054 const unsigned n_element_new_adapted_mesh = new_mesh_pt->nelement();
31055
31056 oomph_info << "CPU for creation of new adapted mesh "
31057 << t_sub_total_create_new_adapted_mesh
31058 << "[nele=" << n_element_new_adapted_mesh << "] (iter "
31059 << iter << "): " << t_sub_total_create_new_adapted_mesh
31060 << std::endl;
31061 }
31062
31063#ifdef OOMPH_HAS_MPI
31064 // ------------------------------------------
31065 // DISTRIBUTED MESH: BEGIN
31066 // ------------------------------------------
31067
31068 // This section is only required if we are dealing with
31069 // distributed meshes, otherwise there are not shared boundaries
31070 // overlapping internal boundaries
31071
31072 // Check if necessary to fill boundary elements for those internal
31073 // boundaries that overlap shared boundaries
31074 if (this->nshared_boundary_overlaps_internal_boundary() > 0)
31075 {
31076 // Copy the data structures that indicate which shared
31077 // boundaries are part of an internal boundary
31079 this->shared_boundary_overlaps_internal_boundary();
31080
31081 // Copy the data structure that indicates which are the shared
31082 // boundaries in each processor
31083 new_mesh_pt->shared_boundaries_ids() = this->shared_boundaries_ids();
31084
31085 // Fill the structures for the boundary elements and face indexes
31086 // of the boundary elements
31087 new_mesh_pt
31089 }
31090 // ------------------------------------------
31091 // DISTRIBUTED MESH: END
31092 // ------------------------------------------
31093#endif // #ifdef OOMPH_HAS_MPI
31094
31095 // Snap to curvilinear boundaries (some code duplication as this
31096 // is repeated below but helper function would take so many
31097 // arguments that it's nearly as messy...
31098
31099 // Pass the boundary geometric objects to the new mesh
31100 new_mesh_pt->boundary_geom_object_pt() =
31101 this->boundary_geom_object_pt();
31102
31103 // Reset the boundary coordinates if there is
31104 // a geometric object associated with the boundary
31105 new_mesh_pt->boundary_coordinate_limits() =
31106 this->boundary_coordinate_limits();
31107
31108 const double t_start_third_stage_segments_connectivity =
31110
31111 for (unsigned b = 0; b < n_boundary; b++)
31112 {
31113 // ------------------------------------------
31114 // DISTRIBUTED MESH: BEGIN
31115 // ------------------------------------------
31116
31117 // Before setting up boundary coordinates for the new mesh we
31118 // require to identify the segments with the old mesh to
31119 // assign initial zeta values
31120#ifdef OOMPH_HAS_MPI
31121 if (this->is_mesh_distributed())
31122 {
31123 // Identify the segments of the new mesh with the ones of
31124 // the original mesh
31125 new_mesh_pt
31127 this);
31128 }
31129#endif
31130 // ------------------------------------------
31131 // DISTRIBUTED MESH: END
31132 // ------------------------------------------
31133
31134 // Setup boundary coordinates for boundaries with GeomObject
31135 // associated
31136 if (new_mesh_pt->boundary_geom_object_pt(b) != 0)
31137 {
31138 new_mesh_pt->template setup_boundary_coordinates<ELEMENT>(b);
31139 }
31140 }
31141
31142 t_total_third_stage_segments_connectivity +=
31143 TimingHelpers::timer() - t_start_third_stage_segments_connectivity;
31144
31145 const double t_start_snap_nodes_new_mesh = TimingHelpers::timer();
31146 // Move the nodes on the new boundary onto the old curvilinear
31147 // boundary. If the boundary is straight this will do precisely
31148 // nothing but will be somewhat inefficient
31149 for (unsigned b = 0; b < n_boundary; b++)
31150 {
31151 this->snap_nodes_onto_boundary(new_mesh_pt, b);
31152 }
31153
31154 const double t_sub_total_snap_nodes_new_mesh =
31155 TimingHelpers::timer() - t_start_snap_nodes_new_mesh;
31156
31157 // Add the time to the total snap nodes time
31158 t_total_snap_nodes += t_sub_total_snap_nodes_new_mesh;
31159
31160 if (Print_timings_level_adaptation > 2)
31161 {
31162 oomph_info << "CPU for snapping nodes onto boundaries (new mesh) "
31163 << "(iter " << iter
31164 << "): " << t_sub_total_snap_nodes_new_mesh << std::endl;
31165 }
31166
31167 // Update mesh further?
31168 if (Mesh_update_fct_pt != 0)
31169 {
31170 Mesh_update_fct_pt(new_mesh_pt);
31171 }
31172
31173 // If we have a continuation problem
31174 // any problem in which the timestepper is a "generalisedtimestepper",
31175 // which will have been set by the problem, then ensure
31176 // all data in the new mesh has the appropriate timestepper
31177 if (dynamic_cast<GeneralisedTimeStepper*>(this->Time_stepper_pt))
31178 {
31180 this->Time_stepper_pt, false);
31181 new_mesh_pt->set_mesh_level_time_stepper(this->Time_stepper_pt,
31182 false);
31183 }
31184
31185 // Not done: get ready for another iteration
31186 iter++;
31187 delete tmp_new_mesh_pt;
31188
31189#ifdef OOMPH_HAS_MPI
31190 // Check whether the number of elements that need (un)refinement
31191 // from the previous iteration is the same, if that is the case
31192 // then we mark this processor as done
31193 if (n_ele_need_refinement_iter == n_ele_need_refinement)
31194 {
31195 done = true;
31196 }
31197 // Update the number of elements that require further
31198 // (un)refinement
31199 n_ele_need_refinement = n_ele_need_refinement_iter;
31200#endif // #ifdef OOMPH_HAS_MPI
31201
31202 // ------------------------------------------
31203 // DISTRIBUTED MESH: BEGIN
31204 // ------------------------------------------
31205
31206 // We can only finish the iteration adaptation process if ALL
31207 // the involved processor are marked as done, otherwise, ALL
31208 // processor need to go for another iteration
31209#ifdef OOMPH_HAS_MPI
31210 if (this->is_mesh_distributed())
31211 {
31212 // Time to check whether other processors have finish to adapt
31213 const double t_start_wait_other_processors = TimingHelpers::timer();
31214
31215 // In case that the mesh is distributed it is necessary to
31216 // verify that no processor requires further refinement. If at
31217 // least one processor needs more refinement then all
31218 // processors need to go for another iteration to participate
31219 // in the communications
31220 unsigned this_processor_requires_another_iteration = 1;
31221
31222 // Is this processor done?
31223 if (done)
31224 {
31225 this_processor_requires_another_iteration = 0;
31226 }
31227 int nproc_not_done = this_processor_requires_another_iteration;
31228 // Get the communicator of the mesh
31229 OomphCommunicator* comm_pt = this->communicator_pt();
31230 // Communicate with all procesoors to check whether we need to
31231 // re-iterate
31232 MPI_Allreduce(&this_processor_requires_another_iteration,
31233 &nproc_not_done,
31234 1,
31235 MPI_UNSIGNED,
31236 MPI_SUM,
31237 comm_pt->mpi_comm());
31238 // Are all processors done?
31239 if (nproc_not_done > 0)
31240 {
31242 << "At least one processors requires further refinement. "
31243 << "Go for another iteration." << std::endl;
31244 done = false;
31245 }
31246
31247 // Total to check whether other processors have finish to
31248 // adapt
31249 const double t_sub_total_wait_other_processors =
31250 TimingHelpers::timer() - t_start_wait_other_processors;
31251
31252 // Add to the total timings to check whether other processors
31253 // need to adapt
31254 t_total_wait_other_processors += t_sub_total_wait_other_processors;
31255
31256 if (Print_timings_level_adaptation > 2)
31257 {
31258 oomph_info << "CPU for waiting other processors "
31259 << "(iter " << iter
31260 << "): " << t_sub_total_wait_other_processors
31261 << std::endl;
31262 }
31263
31264 } // if (this->is_mesh_distributed())
31265#endif
31266 // ------------------------------------------
31267 // DISTRIBUTED MESH: END
31268 // ------------------------------------------
31269
31270 if (!done)
31271 {
31272 oomph_info << "Going for another iteration. Current iteration ("
31273 << iter << ")" << std::endl;
31274
31275 // Use the new mesh as the tmp mesh
31276 tmp_new_mesh_pt = new_mesh_pt;
31277 tmp_new_triangulateio = new_mesh_pt->triangulateio_representation();
31278 }
31279
31280 } // end of iteration (while (!done))
31281
31282 // Delete the temporary geometric object representation of the
31283 // current mesh
31284 delete mesh_geom_obj_pt;
31285
31286 oomph_info << "CPU for iterative generation of new mesh (TOTAL): "
31287 << TimingHelpers::timer() - t_iter << std::endl;
31288
31289 if (Print_timings_level_adaptation > 1)
31290 {
31291 oomph_info << "-- CPU for creating new adapted meshes (TOTAL): "
31292 << t_total_create_new_adapted_mesh << std::endl;
31293
31294 oomph_info << "-- CPU for limiting target areas (TOTAL): "
31295 << t_total_limit_target_areas << std::endl;
31296
31297 oomph_info << "-- CPU for transferring target areas (TOTAL): "
31298 << t_total_transfer_target_areas << std::endl;
31299
31300 oomph_info << "-- CPU for waiting other processors (TOTAL): "
31301 << t_total_wait_other_processors << std::endl;
31302 }
31303
31304 // ==============================================================
31305 // END: Transferring of target areas and creation of new mesh
31306 // ==============================================================
31307
31308 // ==============================================================
31309 // BEGIN: Project solution from the old to the new mesh
31310 // ==============================================================
31311
31312 // Check that the projection step is not disabled
31313 if (!Disable_projection)
31314 {
31315 // Take the time for the projection step
31316 double tt_start_projection = TimingHelpers::timer();
31317
31318 // Print info. for tranfering target areas
31319 if (Print_timings_projection)
31320 {
31321 // Switch timings and stats on
31325 }
31326
31327 double t_proj = TimingHelpers::timer();
31328 oomph_info << "About to begin projection.\n";
31329
31330 // Project current solution onto new mesh
31331 //---------------------------------------
31332 ProjectionProblem<ELEMENT>* project_problem_pt =
31334
31335 // Projection requires to be enabled as distributed if working
31336 // with a distributed mesh
31337#ifdef OOMPH_HAS_MPI
31338 if (this->is_mesh_distributed())
31339 {
31340 // ------------------------------------------
31341 // DISTRIBUTED MESH: BEGIN
31342 // ------------------------------------------
31343
31344 // We need to back up the time stepper object since the
31345 // projection class creates a new one
31346 Time* backed_up_time_pt = this->Time_stepper_pt->time_pt();
31347
31348 // Set the projection problem as distributed
31349 project_problem_pt->enable_problem_distributed();
31350
31351 // Pass the time stepper to the projection problem (used when
31352 // setting multi_domain_interation)
31353 project_problem_pt->add_time_stepper_pt(this->Time_stepper_pt);
31354
31355 // Set the mesh used for the projection object
31356 project_problem_pt->mesh_pt() = new_mesh_pt;
31357 // project_problem_pt->disable_suppress_output_during_projection();
31358
31359 // Use iterative solver for projection? By default, an iterative
31360 // solver is used for the projection stage
31361 if (!this->use_iterative_solver_for_projection())
31362 {
31363 project_problem_pt->disable_use_iterative_solver_for_projection();
31364 }
31365
31366 // Do the projection
31367 project_problem_pt->project(this);
31368
31369 // Reset the time stepper object (only affects distributed meshes)
31370 this->Time_stepper_pt->time_pt() = backed_up_time_pt;
31371
31372 // ------------------------------------------
31373 // DISTRIBUTED MESH: END
31374 // ------------------------------------------
31375
31376 } // if (this->is_mesh_distributed())
31377 else
31378#endif // #ifdef OOMPH_HAS_MPI
31379 {
31380 // Set the mesh used for the projection object
31381 project_problem_pt->mesh_pt() = new_mesh_pt;
31382
31383 // project_problem_pt->disable_suppress_output_during_projection();
31384
31385 // Use iterative solver for projection? By default, an iterative
31386 // solver is used for the projection stage
31387 if (!this->use_iterative_solver_for_projection())
31388 {
31389 project_problem_pt->disable_use_iterative_solver_for_projection();
31390 }
31391
31392 // Do the projection
31393 project_problem_pt->project(this);
31394 }
31395
31396 // Reset printing info. for projection
31397 if (Print_timings_projection)
31398 {
31399 // Switch timings and stats off
31403 }
31404
31405 // Get the total time for projection
31406 const double tt_projection =
31407 TimingHelpers::timer() - tt_start_projection;
31408
31409 if (Print_timings_level_adaptation > 1)
31410 {
31411 // Get the number of elements in the old mesh (this)
31412 const unsigned n_element = this->nelement();
31413 // Get the number of elements in the new mesh
31414 const unsigned n_element_new = new_mesh_pt->nelement();
31415 oomph_info << "CPU for projection (in mesh adaptation) "
31416 << "[n_ele_old_mesh=" << n_element
31417 << ", n_ele_new_mesh=" << n_element_new
31418 << "]: " << tt_projection << std::endl;
31419
31420 // ------------------------------------------
31421 // DISTRIBUTED MESH: BEGIN
31422 // ------------------------------------------
31423#ifdef OOMPH_HAS_MPI
31424 if (this->is_mesh_distributed())
31425 {
31426 // The maximum number of elements in the mesh (over all
31427 // processors)
31428 unsigned n_this_element_new = n_element_new;
31429 unsigned n_max_element_new_global = 0;
31430 // Get the maximum number of elements over all processors
31431 MPI_Reduce(&n_this_element_new,
31432 &n_max_element_new_global,
31433 1,
31434 MPI_UNSIGNED,
31435 MPI_MAX,
31436 0,
31437 this->communicator_pt()->mpi_comm());
31438
31439 // The time for projection for this processor
31440 double tt_this_projection = tt_projection;
31441 double tt_global_min_projection = 0.0;
31442 double tt_global_max_projection = 0.0;
31443
31444 // Get the minimum and maximum time for projection
31445 MPI_Reduce(&tt_this_projection,
31446 &tt_global_min_projection,
31447 1,
31448 MPI_DOUBLE,
31449 MPI_MIN,
31450 0,
31451 this->communicator_pt()->mpi_comm());
31452 MPI_Reduce(&tt_this_projection,
31453 &tt_global_max_projection,
31454 1,
31455 MPI_DOUBLE,
31456 MPI_MAX,
31457 0,
31458 this->communicator_pt()->mpi_comm());
31459
31460 if (this->communicator_pt()->my_rank() == 0)
31461 {
31462 oomph_info << "CPU for projection global (MIN): "
31463 << tt_global_min_projection << std::endl;
31464 oomph_info << "CPU for projection global (MAX) "
31465 << "[n_max_ele_new_global=" << n_max_element_new_global
31466 << "]: " << tt_global_max_projection << std::endl;
31467
31468 std::cerr << "CPU for projection global (MIN): "
31469 << tt_global_min_projection << std::endl;
31470 std::cerr << "CPU for projection global (MAX): "
31471 << "[n_max_ele_new_global=" << n_max_element_new_global
31472 << "]: " << tt_global_max_projection << std::endl;
31473 }
31474 }
31475#endif // #ifdef OOMPH_HAS_MPI
31476 // ------------------------------------------
31477 // DISTRIBUTED MESH: END
31478 // ------------------------------------------
31479
31480 } // if (Print_timings_level_adaptation>1)
31481
31482 oomph_info << "CPU for projection of solution onto new mesh: "
31483 << TimingHelpers::timer() - t_proj << std::endl;
31484
31485 // Delete the projection problem
31486 delete project_problem_pt;
31487
31488 } // if (!Disable_projection)
31489 else
31490 {
31491 oomph_info << "Projection disabled! The new mesh will contain zeros"
31492 << std::endl;
31493 }
31494
31495 // ==============================================================
31496 // END: Project solution from the old to the new mesh
31497 // ==============================================================
31498
31499 double t_rest = TimingHelpers::timer();
31500
31501 // Flush the old mesh
31502 unsigned nnod = nnode();
31503 for (unsigned j = nnod; j > 0; j--)
31504 {
31505 delete Node_pt[j - 1];
31506 Node_pt[j - 1] = 0;
31507 }
31508 unsigned nel = nelement();
31509 for (unsigned e = nel; e > 0; e--)
31510 {
31511 delete Element_pt[e - 1];
31512 Element_pt[e - 1] = 0;
31513 }
31514
31515 // Now copy back to current mesh
31516 //------------------------------
31517 nnod = new_mesh_pt->nnode();
31518 Node_pt.resize(nnod);
31519 nel = new_mesh_pt->nelement();
31520 Element_pt.resize(nel);
31521 for (unsigned j = 0; j < nnod; j++)
31522 {
31523 Node_pt[j] = new_mesh_pt->node_pt(j);
31524 }
31525 for (unsigned e = 0; e < nel; e++)
31526 {
31527 Element_pt[e] = new_mesh_pt->element_pt(e);
31528 }
31529
31530 // Copy the boundary elements information from the new mesh to the
31531 // original mesh
31532 unsigned nbound = 0;
31533
31534#ifdef OOMPH_HAS_MPI
31535 // If working with a distributed mesh we need to change the number
31536 // of boundaries so that shared boundaries information is also
31537 // copied from the old to the new mesh
31538 if (this->is_mesh_distributed())
31539 {
31540 // The boundaries to be copied include those new ones in the new
31541 // mesh (shared boundaries). This info. is required to
31542 // re-establish the halo/haloed scheme
31543 nbound = new_mesh_pt->nboundary();
31544 // After halo and haloed scheme has been re-established the
31545 // number of boundaries is changed to the original number of
31546 // boundaries
31547 }
31548 else
31549#endif
31550 {
31551 // The original number of boundaries
31552 nbound = n_boundary;
31553 }
31554
31555 Boundary_element_pt.resize(nbound);
31556 Face_index_at_boundary.resize(nbound);
31557 Boundary_node_pt.resize(nbound);
31558 for (unsigned b = 0; b < nbound; b++)
31559 {
31560 unsigned nel = new_mesh_pt->nboundary_element(b);
31561 Boundary_element_pt[b].resize(nel);
31562 Face_index_at_boundary[b].resize(nel);
31563 for (unsigned e = 0; e < nel; e++)
31564 {
31565 Boundary_element_pt[b][e] = new_mesh_pt->boundary_element_pt(b, e);
31566 Face_index_at_boundary[b][e] =
31567 new_mesh_pt->face_index_at_boundary(b, e);
31568 }
31569 unsigned nnod = new_mesh_pt->nboundary_node(b);
31570 Boundary_node_pt[b].resize(nnod);
31571 for (unsigned j = 0; j < nnod; j++)
31572 {
31573 Boundary_node_pt[b][j] = new_mesh_pt->boundary_node_pt(b, j);
31574 }
31575 }
31576
31577 // Also copy over the new boundary and region information
31578 unsigned n_region = new_mesh_pt->nregion();
31579 // Only bother if we have regions
31580 if (n_region > 1)
31581 {
31582 // Deal with the region information first
31583 this->Region_attribute.resize(n_region);
31584 for (unsigned r = 0; r < n_region; r++)
31585 {
31586 this->Region_attribute[r] = new_mesh_pt->region_attribute(r);
31587 // Get the region id
31588 unsigned r_id = static_cast<unsigned>(this->Region_attribute[r]);
31589 // Find the number of elements in the region
31590 unsigned n_region_element = new_mesh_pt->nregion_element(r_id);
31591 this->Region_element_pt[r_id].resize(n_region_element);
31592 for (unsigned e = 0; e < n_region_element; e++)
31593 {
31594 this->Region_element_pt[r_id][e] =
31595 new_mesh_pt->region_element_pt(r_id, e);
31596 }
31597 }
31598
31599 // Now the boundary region information
31600 this->Boundary_region_element_pt.resize(nbound);
31601 this->Face_index_region_at_boundary.resize(nbound);
31602
31603 // Now loop over the boundaries
31604 for (unsigned b = 0; b < nbound; ++b)
31605 {
31606 for (unsigned rr = 0; rr < n_region; rr++)
31607 {
31608 // The region id
31609 unsigned r = static_cast<unsigned>(this->Region_attribute[rr]);
31610
31611 unsigned n_boundary_el_in_region =
31612 new_mesh_pt->nboundary_element_in_region(b, r);
31613
31614 if (n_boundary_el_in_region > 0)
31615 {
31616 // Allocate storage in the map
31617 this->Boundary_region_element_pt[b][r].resize(
31618 n_boundary_el_in_region);
31619 this->Face_index_region_at_boundary[b][r].resize(
31620 n_boundary_el_in_region);
31621
31622 // Copy over the information
31623 for (unsigned e = 0; e < n_boundary_el_in_region; ++e)
31624 {
31625 this->Boundary_region_element_pt[b][r][e] =
31626 new_mesh_pt->boundary_element_in_region_pt(b, r, e);
31627 this->Face_index_region_at_boundary[b][r][e] =
31628 new_mesh_pt->face_index_at_boundary_in_region(b, r, e);
31629 }
31630 }
31631 }
31632 } // End of loop over boundaries
31633
31634 } // End of case when more than one region
31635
31636 // ------------------------------------------
31637 // DISTRIBUTED MESH: BEGIN
31638 // ------------------------------------------
31639
31640 // Re-generate halo(ed) information (only for distributed meshes)
31641#ifdef OOMPH_HAS_MPI
31642 if (this->is_mesh_distributed())
31643 {
31644 // Delete halo(ed) information in the original mesh, the new
31645 // halo(ed) information is generated usign the info. of the new
31646 // mesh
31647 if (this->is_mesh_distributed())
31648 {
31649 this->Halo_node_pt.clear();
31650 this->Root_halo_element_pt.clear();
31651
31652 this->Haloed_node_pt.clear();
31653 this->Root_haloed_element_pt.clear();
31654
31655 this->External_halo_node_pt.clear();
31656 this->External_halo_element_pt.clear();
31657
31658 this->External_haloed_node_pt.clear();
31659 this->External_haloed_element_pt.clear();
31660 }
31661
31662 // Re-establish the shared boundary elements and nodes scheme
31663 // before re-establish halo(ed) information
31664 this->reset_shared_boundary_elements_and_nodes();
31665
31666 // -------------------------------------------------------------
31667 // Remove shared boundary elements and nodes from original
31668 // boundary elements and boundary nodes containers. Shared
31669 // boundary elements and nodes are stored in a special
31670 // container.
31671
31672 // Get the shared boundaries in this processor with any other
31673 // processor
31674 Vector<unsigned> my_rank_shared_boundaries_ids;
31675 this->shared_boundaries_in_this_processor(
31676 my_rank_shared_boundaries_ids);
31677
31678 // Get the number of shared boundaries
31679 const unsigned nmy_rank_shd_bnd = my_rank_shared_boundaries_ids.size();
31680 // Loop over the shared boundaries marked as original boundaries
31681 // in tmp_new_mesh
31682 for (unsigned i = 0; i < nmy_rank_shd_bnd; i++)
31683 {
31684 // Get the boundary id
31685 const unsigned shd_bnd_id = my_rank_shared_boundaries_ids[i];
31686 // Flush any previous relation of shared boundary elements
31687 // marked as original boundary elements in tmp_new_mesh
31688 this->Boundary_element_pt[shd_bnd_id].clear();
31689
31690 // Get the number of nodes associated with the original
31691 // boundary in tmp_new_mesh that is a shared boundary
31692 const unsigned tmp_nnodes = this->nshared_boundary_node(shd_bnd_id);
31693 for (unsigned n = 0; n < tmp_nnodes; n++)
31694 {
31695 Node* tmp_node_pt = this->boundary_node_pt(shd_bnd_id, n);
31696 tmp_node_pt->remove_from_boundary(shd_bnd_id);
31697 } // for (n < nnodes)
31698
31699 } // for (shd_bnd_id < nmy_rank_shd_bnd)
31700
31701 // Re-set the number of boundaries to the original one
31702 this->set_nboundary(n_boundary);
31703
31704 // Sort the nodes on the boundaries so that they have the same
31705 // order on all the boundaries
31706 this->sort_nodes_on_shared_boundaries();
31707
31708 // Re-set the halo(ed) scheme
31709 this->reset_halo_haloed_scheme();
31710
31711 // Set the correct number of segments for the boundaries with
31712 // geom objects associated
31713 for (unsigned b = 0; b < n_boundary; b++)
31714 {
31715 if (this->boundary_geom_object_pt(b) != 0)
31716 {
31717 const unsigned nsegments = new_mesh_pt->nboundary_segment(b);
31718 this->set_nboundary_segment_node(b, nsegments);
31719 }
31720 }
31721
31722 // Resume the connections in boundaries were it was suspended
31723 resume_boundary_connections(resume_initial_connection_polyline_pt,
31724 resume_final_connection_polyline_pt);
31725
31726 } // if (this->is_mesh_distributed())
31727
31728#endif // #ifdef OOMPH_HAS_MPI
31729
31730 // ------------------------------------------
31731 // DISTRIBUTED MESH: END
31732 // ------------------------------------------
31733
31734 // Snap the newly created nodes onto any geometric objects
31735 this->snap_nodes_onto_geometric_objects();
31736
31737 // Copy the IDs of the vertex nodes
31738 this->Oomph_vertex_nodes_id = new_mesh_pt->oomph_vertex_nodes_id();
31739
31740 // Copy TriangulateIO representation
31741 TriangleHelper::clear_triangulateio(this->Triangulateio);
31742 bool quiet = true;
31743 this->Triangulateio =
31745 new_mesh_pt->triangulateio_representation(), quiet);
31746
31747 // Flush the mesh
31748 new_mesh_pt->flush_element_and_node_storage();
31749
31750 // Delete the mesh
31751 delete new_mesh_pt;
31752
31753 // Resume of timings
31754 if (Print_timings_level_adaptation > 2)
31755 {
31756 // Report timings related with setting boundary coordinates of
31757 // nodes on segments
31758 oomph_info << "CPU for segments connectivity (first stage) [sec]: "
31759 << t_total_first_stage_segments_connectivity << std::endl;
31760 oomph_info << "CPU for segments connectivity (second stage) [sec]: "
31761 << t_total_second_stage_segments_connectivity << std::endl;
31762 oomph_info << "CPU for segments connectivity (third stage) [sec]: "
31763 << t_total_third_stage_segments_connectivity << std::endl;
31764 }
31765
31766 if (Print_timings_level_adaptation > 1)
31767 {
31768 const double t_total_segments_connectivity =
31769 t_total_first_stage_segments_connectivity +
31770 t_total_second_stage_segments_connectivity +
31771 t_total_third_stage_segments_connectivity;
31772
31773 oomph_info << "CPU for segments connectivity (TOTAL) [sec]: "
31774 << t_total_segments_connectivity << std::endl;
31775
31776 if (Print_timings_level_adaptation > 2)
31777 {
31778 // Report timings for snapping of nodes onto boundaries
31779 oomph_info << "CPU for snapping nodes onto boundaries "
31780 << "(new mesh): " << t_total_snap_nodes << std::endl;
31781 }
31782
31783 t_total_snap_nodes += t_total_snap_nodes_bg_mesh;
31784 oomph_info << "CPU for snapping nodes onto boundaries (TOTAL): "
31785 << t_total_snap_nodes << std::endl;
31786 }
31787
31788 double max_area = 0.0;
31789 double min_area = 0.0;
31790
31791 this->max_and_min_element_size(max_area, min_area);
31792 oomph_info << "Max/min element size in adapted mesh: " << max_area << " "
31793 << min_area << std::endl;
31794
31795 oomph_info << "CPU time for final bits [sec]: "
31796 << TimingHelpers::timer() - t_rest << std::endl;
31797 }
31798 else
31799 {
31800 oomph_info << "Not enough benefit in adaptation.\n";
31801 Nrefined = 0;
31802 Nunrefined = 0;
31803 }
31804
31805 double CPU_for_adaptation = TimingHelpers::timer() - t_start_overall;
31806 oomph_info << "CPU time for adaptation [sec]: " << CPU_for_adaptation
31807 << std::endl;
31808
31809 // ------------------------------------------
31810 // DISTRIBUTED MESH: BEGIN
31811 // ------------------------------------------
31812#ifdef OOMPH_HAS_MPI
31813 if (this->is_mesh_distributed())
31814 {
31815 // Get the communicator
31816 OomphCommunicator* comm_pt = this->communicator_pt();
31817 // Get the total number of processors to compute the average
31818 const unsigned n_proc = comm_pt->nproc();
31819 if (Print_timings_level_adaptation > 1 && n_proc > 1)
31820 {
31821 double global_min_CPU_for_adaptation = 0.0;
31822 double global_max_CPU_for_adaptation = 0.0;
31823 double global_average_CPU_for_adaptation = 0.0;
31824
31825 // Get the maximum and minimum of the adaptation times
31826 MPI_Reduce(&CPU_for_adaptation,
31827 &global_min_CPU_for_adaptation,
31828 1,
31829 MPI_DOUBLE,
31830 MPI_MIN,
31831 0,
31832 comm_pt->mpi_comm());
31833 MPI_Reduce(&CPU_for_adaptation,
31834 &global_max_CPU_for_adaptation,
31835 1,
31836 MPI_DOUBLE,
31837 MPI_MAX,
31838 0,
31839 comm_pt->mpi_comm());
31840 MPI_Reduce(&CPU_for_adaptation,
31841 &global_average_CPU_for_adaptation,
31842 1,
31843 MPI_DOUBLE,
31844 MPI_SUM,
31845 0,
31846 comm_pt->mpi_comm());
31847
31848 // Get the rank of the processor
31849 const unsigned my_rank = comm_pt->my_rank();
31850 if (my_rank == 0)
31851 {
31852 oomph_info << "CPU for adaptation (MIN): "
31853 << global_min_CPU_for_adaptation << std::endl;
31854 oomph_info << "CPU for adaptation (MAX): "
31855 << global_max_CPU_for_adaptation << std::endl;
31856 oomph_info << "CPU for adaptation (AVERAGE): "
31857 << global_average_CPU_for_adaptation / n_proc << std::endl;
31858 } // if (my_rank==0)
31859
31860 } // if (Print_timings_level_adaptation>1&&n_proc>1)
31861
31862 } // if (this->is_mesh_distributed())
31863
31864 // ------------------------------------------
31865 // DISTRIBUTED MESH: END
31866 // ------------------------------------------
31867
31868#endif // #ifdef OOMPH_HAS_MPI
31869 }
31870
31871 //=========================================================================
31872 /// Mark the vertices that are not allowed for deletion by
31873 /// the unrefienment/refinement polyline methods. In charge of
31874 /// filling the Boundary_connections_pt structure
31875 //=========================================================================
31876 template<class ELEMENT>
31878 {
31879 // Clear any previous information
31880 // Boundary_chunk_connections_pt.clear();
31881 Boundary_connections_pt.clear();
31882
31883 // Loop over the boundaries in the domain (outer, internal -- closed
31884 // and open ---, and shared) and get the boundaries ids with
31885 // connections (have or receive)
31886
31887 // Store the boundaries ids that have or receive connection
31888 std::set<unsigned> boundary_id_with_connections;
31889
31890 // ------------------------------------------------------------------
31891 // Outer boundaries
31892 // ------------------------------------------------------------------
31893
31894 // Get the number of outer boundaries (closed boundaries)
31895 const unsigned n_outer_boundaries = this->Outer_boundary_pt.size();
31896
31897 // Loop over the outer boundaries
31898 for (unsigned i = 0; i < n_outer_boundaries; i++)
31899 {
31900 // Get a temporary polygon representation
31901 TriangleMeshPolygon* tmp_polygon_pt = this->Outer_boundary_pt[i];
31902 // Get the number of polylines associated to the current outer
31903 // boundary
31904 const unsigned n_polyline = tmp_polygon_pt->npolyline();
31905 // Loop over the polylines
31906 for (unsigned p = 0; p < n_polyline; p++)
31907 {
31908 // Get a temporary representation of the polyline
31909 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
31910
31911 // Is the initial vertex connected?
31912 if (tmp_polyline_pt->is_initial_vertex_connected())
31913 {
31914 // Get the boundary id of the current polyline
31915 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
31916
31917 // Include the boundary id to the set of boundaries with
31918 // connections
31919 boundary_id_with_connections.insert(bnd_id);
31920
31921 // Boundary id to which the curve is connecte
31922 const unsigned dst_bnd_id =
31923 tmp_polyline_pt->initial_vertex_connected_bnd_id();
31924
31925 // Include the destination boundary id to the set of
31926 // boundaries with connections
31927 boundary_id_with_connections.insert(dst_bnd_id);
31928
31929 } // if (tmp_polyline_pt->is_initial_vertex_connected())
31930
31931 // Is the final vertex connected?
31932 if (tmp_polyline_pt->is_final_vertex_connected())
31933 {
31934 // Get the boundary id of the current polyline
31935 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
31936
31937 // Include the boundary id to the set of boundaries with
31938 // connections
31939 boundary_id_with_connections.insert(bnd_id);
31940
31941 // Boundary id to which the curve is connected
31942 const unsigned dst_bnd_id =
31943 tmp_polyline_pt->final_vertex_connected_bnd_id();
31944
31945 // Include the destination boundary id to the set of
31946 // boundaries with connections
31947 boundary_id_with_connections.insert(dst_bnd_id);
31948
31949 } // if (tmp_polyline_pt->is_final_vertex_connected())
31950
31951 } // for (p < n_polyline)
31952
31953 } // for (i < n_outer_boundaries)
31954
31955 // ------------------------------------------------------------------
31956 // Internal boundaries
31957 // ------------------------------------------------------------------
31958
31959 // Get the number of internal boundaries (closed boundaries)
31960 const unsigned n_internal_boundaries = this->Internal_polygon_pt.size();
31961
31962 // Loop over the internal boundaries
31963 for (unsigned i = 0; i < n_internal_boundaries; i++)
31964 {
31965 // Get a temporary polygon representation
31966 TriangleMeshPolygon* tmp_polygon_pt = this->Internal_polygon_pt[i];
31967 // Get the number of polylines associated to the current internal
31968 // boundary
31969 const unsigned n_polyline = tmp_polygon_pt->npolyline();
31970 // Loop over the polylines
31971 for (unsigned p = 0; p < n_polyline; p++)
31972 {
31973 // Get a temporary representation of the polyline
31974 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
31975
31976 // Is the initial vertex connected?
31977 if (tmp_polyline_pt->is_initial_vertex_connected())
31978 {
31979 // Get the boundary id of the current polyline
31980 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
31981
31982 // Include the boundary id to the set of boundaries with
31983 // connections
31984 boundary_id_with_connections.insert(bnd_id);
31985
31986 // Boundary id to which the curve is connecte
31987 const unsigned dst_bnd_id =
31988 tmp_polyline_pt->initial_vertex_connected_bnd_id();
31989
31990 // Include the destination boundary id to the set of
31991 // boundaries with connections
31992 boundary_id_with_connections.insert(dst_bnd_id);
31993
31994 } // if (tmp_polyline_pt->is_initial_vertex_connected())
31995
31996 // Is the final vertex connected?
31997 if (tmp_polyline_pt->is_final_vertex_connected())
31998 {
31999 // Get the boundary id of the current polyline
32000 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32001
32002 // Include the boundary id to the set of boundaries with
32003 // connections
32004 boundary_id_with_connections.insert(bnd_id);
32005
32006 // Boundary id to which the curve is connected
32007 const unsigned dst_bnd_id =
32008 tmp_polyline_pt->final_vertex_connected_bnd_id();
32009
32010 // Include the destination boundary id to the set of
32011 // boundaries with connections
32012 boundary_id_with_connections.insert(dst_bnd_id);
32013
32014 } // if (tmp_polyline_pt->is_final_vertex_connected())
32015
32016 } // for (p < n_polyline)
32017
32018 } // for (i < n_internal_boundaries)
32019
32020 // ------------------------------------------------------------------
32021 // Open boundaries (nonclosed internal boundaries)
32022 // ------------------------------------------------------------------
32023
32024 // Get the number of internal boundaries (open boundaries)
32025 const unsigned n_open_boundaries = this->Internal_open_curve_pt.size();
32026
32027 // Loop over the internal open boundaries
32028 for (unsigned i = 0; i < n_open_boundaries; i++)
32029 {
32030 // Get a temporary representation for the open curve
32031 TriangleMeshOpenCurve* tmp_open_curve_pt =
32032 this->Internal_open_curve_pt[i];
32033
32034 // Get the number of curve sections associated to the current
32035 // internal open boundary
32036 const unsigned n_curve_section = tmp_open_curve_pt->ncurve_section();
32037
32038 // Loop over the curve section
32039 for (unsigned p = 0; p < n_curve_section; p++)
32040 {
32041 // Get a temporary representation of the curve section
32042 // (polyline)
32043 TriangleMeshPolyLine* tmp_polyline_pt =
32044 tmp_open_curve_pt->polyline_pt(p);
32045
32046 // Is the initial vertex connected?
32047 if (tmp_polyline_pt->is_initial_vertex_connected())
32048 {
32049 // Get the boundary id of the current polyline
32050 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32051
32052 // Include the boundary id to the set of boundaries with
32053 // connections
32054 boundary_id_with_connections.insert(bnd_id);
32055
32056 // Boundary id to which the curve is connecte
32057 const unsigned dst_bnd_id =
32058 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32059
32060 // Include the destination boundary id to the set of
32061 // boundaries with connections
32062 boundary_id_with_connections.insert(dst_bnd_id);
32063
32064 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32065
32066 // Is the final vertex connected?
32067 if (tmp_polyline_pt->is_final_vertex_connected())
32068 {
32069 // Get the boundary id of the current polyline
32070 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32071
32072 // Include the boundary id to the set of boundaries with
32073 // connections
32074 boundary_id_with_connections.insert(bnd_id);
32075
32076 // Boundary id to which the curve is connected
32077 const unsigned dst_bnd_id =
32078 tmp_polyline_pt->final_vertex_connected_bnd_id();
32079
32080 // Include the destination boundary id to the set of
32081 // boundaries with connections
32082 boundary_id_with_connections.insert(dst_bnd_id);
32083
32084 } // if (tmp_polyline_pt->is_final_vertex_connected())
32085
32086 } // for (p < n_curve_section)
32087
32088 } // for (i < n_open_boundaries)
32089
32090#ifdef OOMPH_HAS_MPI
32091 // ------------------------------------------------------------------
32092 // Shared boundaries (only for distributed meshes)
32093 // ------------------------------------------------------------------
32094
32095 // Check if we need to include any information associated with
32096 // shared boundaries
32097 if (this->is_mesh_distributed())
32098 {
32099 // Get the rank of the current processor
32100 const unsigned my_rank = this->communicator_pt()->my_rank();
32101
32102 // Get the number of shared curves in the current processor
32103 const unsigned n_shared_curves = this->nshared_boundary_curves(my_rank);
32104
32105 // Loop over the shared curves
32106 for (unsigned i = 0; i < n_shared_curves; i++)
32107 {
32108 // Get the number of polylines associated to the current shared
32109 // curve
32110 const unsigned n_polyline = this->nshared_boundary_polyline(my_rank, i);
32111
32112 // Loop over the polylines associated to the current shared
32113 // curve
32114 for (unsigned p = 0; p < n_polyline; p++)
32115 {
32116 // Get a temporary representation of the shared polyline
32117 TriangleMeshPolyLine* tmp_polyline_pt =
32118 this->shared_boundary_polyline_pt(my_rank, i, p);
32119
32120 // Is the initial vertex connected?
32121 if (tmp_polyline_pt->is_initial_vertex_connected())
32122 {
32123 // Get the boundary id of the current polyline
32124 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32125
32126 // Include the boundary id to the set of boundaries with
32127 // connections
32128 boundary_id_with_connections.insert(bnd_id);
32129
32130 // Boundary id to which the curve is connecte
32131 const unsigned dst_bnd_id =
32132 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32133
32134 // Include the destination boundary id to the set of
32135 // boundaries with connections
32136 boundary_id_with_connections.insert(dst_bnd_id);
32137
32138 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32139
32140 // Is the final vertex connected?
32141 if (tmp_polyline_pt->is_final_vertex_connected())
32142 {
32143 // Get the boundary id of the current polyline
32144 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32145
32146 // Include the boundary id to the set of boundaries with
32147 // connections
32148 boundary_id_with_connections.insert(bnd_id);
32149
32150 // Boundary id to which the curve is connected
32151 const unsigned dst_bnd_id =
32152 tmp_polyline_pt->final_vertex_connected_bnd_id();
32153
32154 // Include the destination boundary id to the set of
32155 // boundaries with connections
32156 boundary_id_with_connections.insert(dst_bnd_id);
32157
32158 } // if (tmp_polyline_pt->is_final_vertex_connected())
32159
32160 } // for (p < n_polyline)
32161
32162 } // for (i < n_shared_curves)
32163
32164 } // if (this->is_mesh_distributed())
32165
32166#endif // #ifdef OOMPH_HAS_MPI
32167
32168 // ---------------------------------------------------------------
32169 // Get the nodes sorted by segments of the boundaries with
32170 // connections
32171
32172 // Store the sorted nodes by segments of the boundaries with
32173 // connections
32174 std::map<unsigned, Vector<Vector<Node*>>> bnd_sorted_segment_node_pt;
32175
32176 // Loop over the boundaries with connections
32177 for (std::set<unsigned>::iterator it = boundary_id_with_connections.begin();
32178 it != boundary_id_with_connections.end();
32179 it++)
32180 {
32181 // Get the boundary id
32182 const unsigned bnd_id = (*it);
32183#ifdef OOMPH_HAS_MPI
32184 // Working with a distributed mesh
32185 if (this->is_mesh_distributed())
32186 {
32187 // Get the initial shared boundary id
32188 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
32189 // Is an original or shared boundary
32190 if (bnd_id >= init_shd_bnd_id)
32191 {
32192 // Is a shared boundary
32193
32194 // Temporary storage for the nodes on the shared boundary
32195 Vector<Vector<Node*>> tmp_shared_nodes_pt;
32196
32197 // Get the nodes associated to the shared boundary
32198 get_shared_boundary_segment_nodes_helper(bnd_id, tmp_shared_nodes_pt);
32199
32200 // Store the nodes associated to the shared boundary
32201 bnd_sorted_segment_node_pt[bnd_id] = tmp_shared_nodes_pt;
32202
32203 } // if (bnd_id >= init_shd_bnd_id)
32204 else
32205 {
32206 // Is an original boundary
32207
32208 // Temporary storage for the nodes on the original boundary
32209 Vector<Vector<Node*>> tmp_boundary_nodes_pt;
32210
32211 // Get the nodes associated to the shared boundary
32212 get_boundary_segment_nodes_helper(bnd_id, tmp_boundary_nodes_pt);
32213
32214 // Store the nodes associated to the shared boundary
32215 bnd_sorted_segment_node_pt[bnd_id] = tmp_boundary_nodes_pt;
32216
32217 } // if (bnd_id >= init_shd_bnd_id)
32218
32219 } // if (this->is_mesh_distributed())
32220 else
32221#endif // #ifdef OOMPH_HAS_MPI
32222 {
32223 // Is an original boundary
32224
32225 // Temporary storage for the nodes on the original boundary
32226 Vector<Vector<Node*>> tmp_boundary_nodes_pt;
32227
32228 // Get the nodes associated to the shared boundary
32229 get_boundary_segment_nodes_helper(bnd_id, tmp_boundary_nodes_pt);
32230
32231 // Store the nodes associated to the shared boundary
32232 bnd_sorted_segment_node_pt[bnd_id] = tmp_boundary_nodes_pt;
32233
32234 } // if (this->is_mesh_distributed())
32235
32236 } // Loop over boundaries with connections
32237
32238 // -----------------------------------------------------------------
32239 // Loop again over the boundaries (original and shared) and search
32240 // for the repeated nodes in those boundaries with connections
32241
32242 // ------------------------------------------------------------------
32243 // Outer boundaries
32244 // ------------------------------------------------------------------
32245 // Loop over the outer boundaries
32246 for (unsigned i = 0; i < n_outer_boundaries; i++)
32247 {
32248 // Get a temporary polygon representation
32249 TriangleMeshPolygon* tmp_polygon_pt = this->Outer_boundary_pt[i];
32250 // Get the number of polylines associated to the current outer
32251 // boundary
32252 const unsigned n_polyline = tmp_polygon_pt->npolyline();
32253 // Loop over the polylines
32254 for (unsigned p = 0; p < n_polyline; p++)
32255 {
32256 // Get a temporary representation of the polyline
32257 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
32258
32259 // Is the initial vertex connected?
32260 if (tmp_polyline_pt->is_initial_vertex_connected())
32261 {
32262 // Get the boundary id of the current polyline
32263 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32264
32265 // Boundary id to which the curve is connected
32266 const unsigned dst_bnd_id =
32267 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32268
32269 // Boundary chunk to which the curve is connected
32270 const unsigned dst_chunk =
32271 tmp_polyline_pt->initial_vertex_connected_n_chunk();
32272
32273 // Get the nodes representation of the current boundary
32274 Vector<Vector<Node*>> src_bnd_node_pt =
32275 bnd_sorted_segment_node_pt[bnd_id];
32276
32277 // Get the nodes representation of the boundary to connect
32278 Vector<Vector<Node*>> dst_bnd_node_pt =
32279 bnd_sorted_segment_node_pt[dst_bnd_id];
32280
32281 // Add the repeated node to the list of non delete-able
32282 // vertices
32283 add_non_delete_vertices_from_boundary_helper(
32284 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32285
32286 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32287
32288 // Is the final vertex connected?
32289 if (tmp_polyline_pt->is_final_vertex_connected())
32290 {
32291 // Get the boundary id of the current polyline
32292 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32293
32294 // Boundary id to which the curve is connected
32295 const unsigned dst_bnd_id =
32296 tmp_polyline_pt->final_vertex_connected_bnd_id();
32297
32298 // Boundary chunk to which the curve is connected
32299 const unsigned dst_chunk =
32300 tmp_polyline_pt->final_vertex_connected_n_chunk();
32301
32302 // Get the nodes representation of the current boundary
32303 Vector<Vector<Node*>> src_bnd_node_pt =
32304 bnd_sorted_segment_node_pt[bnd_id];
32305
32306 // Get the nodes representation of the boundary to connect
32307 Vector<Vector<Node*>> dst_bnd_node_pt =
32308 bnd_sorted_segment_node_pt[dst_bnd_id];
32309
32310 // Add the repeated node to the list of non delete-able
32311 // vertices
32312 add_non_delete_vertices_from_boundary_helper(
32313 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32314
32315 } // if (tmp_polyline_pt->is_final_vertex_connected())
32316
32317 } // for (p < n_polyline)
32318
32319 } // for (i < n_outer_boundaries)
32320
32321 // ------------------------------------------------------------------
32322 // Internal boundaries
32323 // ------------------------------------------------------------------
32324 // Loop over the internal boundaries
32325 for (unsigned i = 0; i < n_internal_boundaries; i++)
32326 {
32327 // Get a temporary polygon representation
32328 TriangleMeshPolygon* tmp_polygon_pt = this->Internal_polygon_pt[i];
32329 // Get the number of polylines associated to the current internal
32330 // boundary
32331 const unsigned n_polyline = tmp_polygon_pt->npolyline();
32332 // Loop over the polylines
32333 for (unsigned p = 0; p < n_polyline; p++)
32334 {
32335 // Get a temporary representation of the polyline
32336 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
32337
32338 // Is the initial vertex connected?
32339 if (tmp_polyline_pt->is_initial_vertex_connected())
32340 {
32341 // Get the boundary id of the current polyline
32342 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32343
32344 // Boundary id to which the curve is connected
32345 const unsigned dst_bnd_id =
32346 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32347
32348 // Boundary chunk to which the curve is connected
32349 const unsigned dst_chunk =
32350 tmp_polyline_pt->initial_vertex_connected_n_chunk();
32351
32352 // Get the nodes representation of the current boundary
32353 Vector<Vector<Node*>> src_bnd_node_pt =
32354 bnd_sorted_segment_node_pt[bnd_id];
32355
32356 // Get the nodes representation of the boundary to connect
32357 Vector<Vector<Node*>> dst_bnd_node_pt =
32358 bnd_sorted_segment_node_pt[dst_bnd_id];
32359
32360 // Add the repeated node to the list of non delete-able
32361 // vertices
32362 add_non_delete_vertices_from_boundary_helper(
32363 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32364
32365 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32366
32367 // Is the final vertex connected?
32368 if (tmp_polyline_pt->is_final_vertex_connected())
32369 {
32370 // Get the boundary id of the current polyline
32371 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32372
32373 // Boundary id to which the curve is connected
32374 const unsigned dst_bnd_id =
32375 tmp_polyline_pt->final_vertex_connected_bnd_id();
32376
32377 // Boundary chunk to which the curve is connected
32378 const unsigned dst_chunk =
32379 tmp_polyline_pt->final_vertex_connected_n_chunk();
32380
32381 // Get the nodes representation of the current boundary
32382 Vector<Vector<Node*>> src_bnd_node_pt =
32383 bnd_sorted_segment_node_pt[bnd_id];
32384
32385 // Get the nodes representation of the boundary to connect
32386 Vector<Vector<Node*>> dst_bnd_node_pt =
32387 bnd_sorted_segment_node_pt[dst_bnd_id];
32388
32389 // Add the repeated node to the list of non delete-able
32390 // vertices
32391 add_non_delete_vertices_from_boundary_helper(
32392 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32393
32394 } // if (tmp_polyline_pt->is_final_vertex_connected())
32395
32396 } // for (p < n_polyline)
32397
32398 } // for (i < n_internal_boundaries)
32399
32400 // ------------------------------------------------------------------
32401 // Open boundaries (nonclosed internal boundaries)
32402 // ------------------------------------------------------------------
32403 // Loop over the internal open boundaries
32404 for (unsigned i = 0; i < n_open_boundaries; i++)
32405 {
32406 // Get a temporary representation for the open curve
32407 TriangleMeshOpenCurve* tmp_open_curve_pt =
32408 this->Internal_open_curve_pt[i];
32409
32410 // Get the number of curve sections associated to the current
32411 // internal open boundary
32412 const unsigned n_curve_section = tmp_open_curve_pt->ncurve_section();
32413
32414 // Loop over the curve section
32415 for (unsigned p = 0; p < n_curve_section; p++)
32416 {
32417 // Get a temporary representation of the curve section
32418 // (polyline)
32419 TriangleMeshPolyLine* tmp_polyline_pt =
32420 tmp_open_curve_pt->polyline_pt(p);
32421
32422 // Is the initial vertex connected?
32423 if (tmp_polyline_pt->is_initial_vertex_connected())
32424 {
32425 // Get the boundary id of the current polyline
32426 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32427
32428 // Boundary id to which the curve is connected
32429 const unsigned dst_bnd_id =
32430 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32431
32432 // Boundary chunk to which the curve is connected
32433 const unsigned dst_chunk =
32434 tmp_polyline_pt->initial_vertex_connected_n_chunk();
32435
32436 // Get the nodes representation of the current boundary
32437 Vector<Vector<Node*>> src_bnd_node_pt =
32438 bnd_sorted_segment_node_pt[bnd_id];
32439
32440 // Get the nodes representation of the boundary to connect
32441 Vector<Vector<Node*>> dst_bnd_node_pt =
32442 bnd_sorted_segment_node_pt[dst_bnd_id];
32443
32444 // Add the repeated node to the list of non delete-able
32445 // vertices
32446 add_non_delete_vertices_from_boundary_helper(
32447 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32448
32449 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32450
32451 // Is the final vertex connected?
32452 if (tmp_polyline_pt->is_final_vertex_connected())
32453 {
32454 // Get the boundary id of the current polyline
32455 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32456
32457 // Boundary id to which the curve is connected
32458 const unsigned dst_bnd_id =
32459 tmp_polyline_pt->final_vertex_connected_bnd_id();
32460
32461 // Boundary chunk to which the curve is connected
32462 const unsigned dst_chunk =
32463 tmp_polyline_pt->final_vertex_connected_n_chunk();
32464
32465 // Get the nodes representation of the current boundary
32466 Vector<Vector<Node*>> src_bnd_node_pt =
32467 bnd_sorted_segment_node_pt[bnd_id];
32468
32469 // Get the nodes representation of the boundary to connect
32470 Vector<Vector<Node*>> dst_bnd_node_pt =
32471 bnd_sorted_segment_node_pt[dst_bnd_id];
32472
32473 // Add the repeated node to the list of non delete-able
32474 // vertices
32475 add_non_delete_vertices_from_boundary_helper(
32476 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32477
32478 } // if (tmp_polyline_pt->is_final_vertex_connected())
32479
32480 } // for (p < n_curve_section)
32481
32482 } // for (i < n_open_boundaries)
32483
32484#ifdef OOMPH_HAS_MPI
32485 // ------------------------------------------------------------------
32486 // Shared boundaries (only for distributed meshes)
32487 // ------------------------------------------------------------------
32488
32489 // Check if we need to include any information associated with
32490 // shared boundaries
32491 if (this->is_mesh_distributed())
32492 {
32493 // Get the rank of the current processor
32494 const unsigned my_rank = this->communicator_pt()->my_rank();
32495
32496 // Get the number of shared curves in the current processor
32497 const unsigned n_shared_curves = this->nshared_boundary_curves(my_rank);
32498
32499 // Loop over the shared curves
32500 for (unsigned i = 0; i < n_shared_curves; i++)
32501 {
32502 // Get the number of polylines associated to the current shared
32503 // curve
32504 const unsigned n_polyline = this->nshared_boundary_polyline(my_rank, i);
32505
32506 // Loop over the polylines associated to the current shared
32507 // curve
32508 for (unsigned p = 0; p < n_polyline; p++)
32509 {
32510 // Get a temporary representation of the shared polyline
32511 TriangleMeshPolyLine* tmp_polyline_pt =
32512 this->shared_boundary_polyline_pt(my_rank, i, p);
32513
32514 // Is the initial vertex connected?
32515 if (tmp_polyline_pt->is_initial_vertex_connected())
32516 {
32517 // Get the boundary id of the current polyline
32518 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32519
32520 // Boundary id to which the curve is connected
32521 const unsigned dst_bnd_id =
32522 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32523
32524 // Boundary chunk to which the curve is connected
32525 const unsigned dst_chunk =
32526 tmp_polyline_pt->initial_vertex_connected_n_chunk();
32527
32528 // Get the nodes representation of the current boundary
32529 Vector<Vector<Node*>> src_bnd_node_pt =
32530 bnd_sorted_segment_node_pt[bnd_id];
32531
32532 // Get the nodes representation of the boundary to connect
32533 Vector<Vector<Node*>> dst_bnd_node_pt =
32534 bnd_sorted_segment_node_pt[dst_bnd_id];
32535
32536 // Add the repeated node to the list of non delete-able
32537 // vertices
32538 add_non_delete_vertices_from_boundary_helper(
32539 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32540
32541 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32542
32543 // Is the final vertex connected?
32544 if (tmp_polyline_pt->is_final_vertex_connected())
32545 {
32546 // Get the boundary id of the current polyline
32547 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32548
32549 // Boundary id to which the curve is connected
32550 const unsigned dst_bnd_id =
32551 tmp_polyline_pt->final_vertex_connected_bnd_id();
32552
32553 // Boundary chunk to which the curve is connected
32554 const unsigned dst_chunk =
32555 tmp_polyline_pt->final_vertex_connected_n_chunk();
32556
32557 // Get the nodes representation of the current boundary
32558 Vector<Vector<Node*>> src_bnd_node_pt =
32559 bnd_sorted_segment_node_pt[bnd_id];
32560
32561 // Get the nodes representation of the boundary to connect
32562 Vector<Vector<Node*>> dst_bnd_node_pt =
32563 bnd_sorted_segment_node_pt[dst_bnd_id];
32564
32565 // Add the repeated node to the list of non delete-able
32566 // vertices
32567 add_non_delete_vertices_from_boundary_helper(
32568 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32569
32570 } // if (tmp_polyline_pt->is_final_vertex_connected())
32571
32572 } // for (p < n_polyline)
32573
32574 } // for (i < n_shared_curves)
32575
32576 } // if (this->is_mesh_distributed())
32577
32578#endif // #ifdef OOMPH_HAS_MPI
32579 }
32580
32581 //=========================================================================
32582 /// Adds the vertices from the sources boundary that are
32583 /// repeated in the destination boundary to the list of non
32584 /// delete-able vertices in the destination boundary
32585 //=========================================================================
32586 template<class ELEMENT>
32589 Vector<Vector<Node*>> src_bound_segment_node_pt,
32590 Vector<Vector<Node*>> dst_bound_segment_node_pt,
32591 const unsigned& dst_bnd_id,
32592 const unsigned& dst_bnd_chunk)
32593 {
32594 // Get the number of segments in the source boundary
32595 const unsigned n_seg = src_bound_segment_node_pt.size();
32596 // Loop over the segments in the source boundary
32597 for (unsigned iseg = 0; iseg < n_seg; iseg++)
32598 {
32599 // Get the number of nodes in the current segment
32600 const unsigned nnode = src_bound_segment_node_pt[iseg].size();
32601 // Get the left and right node of the current segment
32602 Node* left_node_pt = src_bound_segment_node_pt[iseg][0];
32603 Node* right_node_pt = src_bound_segment_node_pt[iseg][nnode - 1];
32604
32605 // Get the number of segments in the destination boundary
32606 const unsigned n_dst_seg = dst_bound_segment_node_pt.size();
32607 // Loop over the segments in the destination boundary
32608 for (unsigned jseg = 0; jseg < n_dst_seg; jseg++)
32609 {
32610 // Get the number of nodes on the current destination segment
32611 const unsigned n_dst_node = dst_bound_segment_node_pt[jseg].size();
32612 // Loop over the nodes until the node has been found or we have
32613 // visited all the nodes
32614 for (unsigned jnode = 0; jnode < n_dst_node; jnode++)
32615 {
32616 // Get a pointer to the jnode in the destination segment
32617 // boundary
32618 Node* tmp_node_pt = dst_bound_segment_node_pt[jseg][jnode];
32619 // Is the node the same as the left or right node if
32620 // the source segment boundary
32621 if (tmp_node_pt == left_node_pt)
32622 {
32623 // We have foud the node to connect, get the vertex of the node
32624 Vector<double> vertex(2);
32625 vertex[0] = tmp_node_pt->x(0);
32626 vertex[1] = tmp_node_pt->x(1);
32627
32628 // Establish the vertex coordinate as untouchable in the
32629 // destination boundary during the adaptation process. It
32630 // means that unrefinement can not take off the vertices
32631 // that receive connections in the destination boundary
32632 Boundary_connections_pt[dst_bnd_id].insert(vertex);
32633 // Boundary_chunk_connections_pt[dst_bnd_id][dst_bnd_chunk].
32634 // insert(vertex);
32635
32636 // return
32637 return;
32638
32639 } // if (tmp_node_pt == left_node_pt)
32640 else if (tmp_node_pt == right_node_pt)
32641 {
32642 // We have foud the node to connect, get the vertex of the node
32643 Vector<double> vertex(2);
32644 vertex[0] = tmp_node_pt->x(0);
32645 vertex[1] = tmp_node_pt->x(1);
32646
32647 // Establish the vertex coordinate as untouchable in the
32648 // destination boundary during the adaptation process. It
32649 // means that unrefinement can not take off the vertices
32650 // that receive connections in the destination boundary
32651 // Boundary_chunk_connections_pt[dst_bnd_id][dst_bnd_chunk].
32652 // insert(vertex);
32653 Boundary_connections_pt[dst_bnd_id].insert(vertex);
32654
32655 // return
32656 return;
32657
32658 } // else if (tmp_node_pt == right_node_pt)
32659
32660 } // for (jnode < n_dst_node)
32661
32662 } // for (jseg < n_dst_seg)
32663
32664 } // for (iseg < n_seg)
32665 }
32666
32667#ifdef OOMPH_HAS_MPI
32668 //=========================================================================
32669 /// Synchronise the vertices that are marked for non deletion
32670 // on the shared boundaries. Unrefinement of shared boundaries is
32671 // performed only if the candidate node is not marked for non deletion
32672 //=========================================================================
32673 template<class ELEMENT>
32674 const void RefineableTriangleMesh<
32675 ELEMENT>::synchronize_shared_boundary_connections()
32676 {
32677 // Get the number of processors
32678 const unsigned nproc = this->communicator_pt()->nproc();
32679 // Get my rank
32680 const unsigned my_rank = this->communicator_pt()->my_rank();
32681
32682 // loop over the processors
32683 for (unsigned jproc = 0; jproc < nproc; jproc++)
32684 {
32685 // The number of boundaries shared with the current processor
32686 // (if jproc==my_rank then there are no shared boundaries
32687 // between them)
32688 const unsigned n_shd_bnd_jproc = this->nshared_boundaries(my_rank, jproc);
32689
32690 // Are there shared boundaries with the jproc processor?
32691 // There are no info. with myself
32692 if (jproc != my_rank && n_shd_bnd_jproc > 0)
32693 {
32694 // Storage for the boundaries ids with vertices for non
32695 // deletion
32696 Vector<unsigned> shd_bnd_id_for_non_deletion;
32697
32698 // Storage for chunk numbers of boundaries with vertices
32699 // for non deletion
32700 Vector<unsigned> chunk_for_non_deletion;
32701
32702 // The number of vertices for nondeletion in the shared
32703 // boundaries
32704 Vector<unsigned> number_vertices_non_deletion;
32705
32706 // Vertices marked for nondeletion in shared boundaries
32707 Vector<Vector<Vector<double>>> vertices_for_non_deletion;
32708
32709 // Get the boundary ids of the shared boundaries with jproc
32710 // processor
32711 Vector<unsigned> shd_bnd_ids =
32712 this->shared_boundaries_ids(my_rank, jproc);
32713
32714 // Get the number of shared boundaries with jproc
32715 const unsigned n_shd_bnd_jproc = shd_bnd_ids.size();
32716 // loop over the shared boundaries with jproc
32717 for (unsigned ishd_bnd = 0; ishd_bnd < n_shd_bnd_jproc; ishd_bnd++)
32718 {
32719 // Get the shared boudary id
32720 const unsigned shd_bnd_id = shd_bnd_ids[ishd_bnd];
32721 // Get the associated polyline
32722 TriangleMeshPolyLine* shd_polyline_pt =
32723 this->boundary_polyline_pt(shd_bnd_id);
32724 // Get the chunk number
32725 const unsigned chunk = shd_polyline_pt->boundary_chunk();
32726
32727 // Store the vertices not allowed for deletion
32728 std::set<Vector<double>> no_delete_vertex;
32729
32730 // Does the boundary has vertives for nondeleteion?
32731 const bool boundary_receive_connections =
32732 this->boundary_connections(shd_bnd_id, chunk, no_delete_vertex);
32733
32734 // Get the number of vertices for nondeletion
32735 const unsigned n_non_delete_vertex = no_delete_vertex.size();
32736
32737 // Are there vertices for nondeletion?
32738 if (boundary_receive_connections && n_non_delete_vertex > 0)
32739 {
32740 // Add the shared boundary id
32741 shd_bnd_id_for_non_deletion.push_back(shd_bnd_id);
32742 // Add the chunk number
32743 chunk_for_non_deletion.push_back(chunk);
32744 // Add the number of vertices for non deletion
32745 number_vertices_non_deletion.push_back(n_non_delete_vertex);
32746
32747 // The list of vertices to add
32748 Vector<Vector<double>> tmp_vertices;
32749
32750 // Add the vertices for non deletion
32751 for (std::set<Vector<double>>::iterator it =
32752 no_delete_vertex.begin();
32753 it != no_delete_vertex.end();
32754 it++)
32755 {
32756 // Get the vertex coordinate
32757 Vector<double> vertex = (*it);
32758 tmp_vertices.push_back(vertex);
32759 }
32760
32761 // Add the vertices coordinates to a vector storage
32762 vertices_for_non_deletion.push_back(tmp_vertices);
32763
32764 } // if (boundary_receive_connections && n_non_delete_vertex > 0)
32765
32766 } // for (ishd_bnd<n_shd_bnd_jproc)
32767
32768 // ----------------------------------------------------------
32769 // ----------------------------------------------------------
32770 // ----------------------------------------------------------
32771 // Now send the info. to the other processor (jproc)
32772 // ----------------------------------------------------------
32773 // ----------------------------------------------------------
32774 // ----------------------------------------------------------
32775 // Get the communicator of the mesh
32776 OomphCommunicator* comm_pt = this->communicator_pt();
32777
32778 // Set MPI info
32779 MPI_Status status;
32780 MPI_Request request;
32781
32782 // -----------------------------------------------------------
32783 // Prepare the data
32784 // Get the number of shared boundaires with vertices marked
32785 // for non deletion
32786 const unsigned n_shd_bnd_with_non_delete_vertices =
32787 shd_bnd_id_for_non_deletion.size();
32788
32789 // Size of the package
32790 const unsigned size_package = 3;
32791 // Ndata to send
32792 const unsigned n_unsigned_data_to_send =
32793 n_shd_bnd_with_non_delete_vertices * size_package;
32794 // The flat package to send the info.
32795 Vector<unsigned> flat_package_unsigned_send(n_unsigned_data_to_send);
32796 Vector<double> flat_package_double_send;
32797
32798 Vector<unsigned> flat_package_unsigned_recv;
32799 Vector<double> flat_package_double_recv;
32800
32801 // Prepare the data to be sent
32802 unsigned j = 0;
32803 for (unsigned i = 0; i < n_shd_bnd_with_non_delete_vertices; i++)
32804 {
32805 // The shared boundary id
32806 flat_package_unsigned_send[j++] = shd_bnd_id_for_non_deletion[i];
32807 // The chunk number
32808 flat_package_unsigned_send[j++] = chunk_for_non_deletion[i];
32809 // The number of vertices for nondeletion
32810 flat_package_unsigned_send[j++] = number_vertices_non_deletion[i];
32811 // Also package the vertices
32812 const unsigned n_vertices_non_deletion =
32813 number_vertices_non_deletion[i];
32814 // Loop over the vertices and store them in the flat
32815 // package to be sent
32816 for (unsigned h = 0; h < n_vertices_non_deletion; h++)
32817 {
32818 flat_package_double_send.push_back(
32819 vertices_for_non_deletion[i][h][0]);
32820 flat_package_double_send.push_back(
32821 vertices_for_non_deletion[i][h][1]);
32822 } // for (h<n_vertices_non_deletion)
32823
32824 } // for (i<n_shd_bnd_with_non_delete_vertices)
32825
32826 // ----------------------------------------------------------
32827 int send_proc = jproc;
32828 int recv_proc = jproc;
32829 unsigned send_count_unsigned_values = n_unsigned_data_to_send;
32830 unsigned send_count_double_values = flat_package_double_send.size();
32831 //-----------------------------------------------------------
32832 // Do the transfering of info.
32833 //-----------------------------------------------------------
32834 // Start with UNSIGNED info.
32835 MPI_Isend(&send_count_unsigned_values,
32836 1,
32837 MPI_UNSIGNED,
32838 send_proc,
32839 1,
32840 comm_pt->mpi_comm(),
32841 &request);
32842
32843 unsigned receive_count_unsigned_values = 0;
32844 MPI_Recv(&receive_count_unsigned_values,
32845 1,
32846 MPI_UNSIGNED,
32847 recv_proc,
32848 1,
32849 comm_pt->mpi_comm(),
32850 &status);
32851
32852 MPI_Wait(&request, MPI_STATUS_IGNORE);
32853
32854 // Send the actual data
32855 if (send_count_unsigned_values != 0)
32856 {
32857 MPI_Isend(&flat_package_unsigned_send[0],
32858 send_count_unsigned_values,
32859 MPI_UNSIGNED,
32860 send_proc,
32861 2,
32862 comm_pt->mpi_comm(),
32863 &request);
32864 }
32865
32866 // Receive the actual data
32867 if (receive_count_unsigned_values != 0)
32868 {
32869 flat_package_unsigned_recv.resize(receive_count_unsigned_values);
32870 MPI_Recv(&flat_package_unsigned_recv[0],
32871 receive_count_unsigned_values,
32872 MPI_UNSIGNED,
32873 recv_proc,
32874 2,
32875 comm_pt->mpi_comm(),
32876 &status);
32877 }
32878
32879 // Wait for sending the data and the other processor
32880 // receives
32881 if (send_count_unsigned_values != 0)
32882 {
32883 MPI_Wait(&request, MPI_STATUS_IGNORE);
32884 }
32885
32886 //-----------------------------------------------------------
32887 // Then continue with DOUBLE info.
32888 MPI_Isend(&send_count_double_values,
32889 1,
32890 MPI_UNSIGNED,
32891 send_proc,
32892 1,
32893 comm_pt->mpi_comm(),
32894 &request);
32895
32896 unsigned receive_count_double_values = 0;
32897 MPI_Recv(&receive_count_double_values,
32898 1,
32899 MPI_UNSIGNED,
32900 recv_proc,
32901 1,
32902 comm_pt->mpi_comm(),
32903 &status);
32904
32905 MPI_Wait(&request, MPI_STATUS_IGNORE);
32906
32907 // Send the actual data
32908 if (send_count_double_values != 0)
32909 {
32910 MPI_Isend(&flat_package_double_send[0],
32911 send_count_double_values,
32912 MPI_DOUBLE,
32913 send_proc,
32914 2,
32915 comm_pt->mpi_comm(),
32916 &request);
32917 }
32918
32919 // Receive the actual data
32920 if (receive_count_double_values != 0)
32921 {
32922 flat_package_double_recv.resize(receive_count_double_values);
32923 MPI_Recv(&flat_package_double_recv[0],
32924 receive_count_double_values,
32925 MPI_DOUBLE,
32926 recv_proc,
32927 2,
32928 comm_pt->mpi_comm(),
32929 &status);
32930 }
32931
32932 // Wait for sending the data and the other processor
32933 // receives
32934 if (send_count_double_values != 0)
32935 {
32936 MPI_Wait(&request, MPI_STATUS_IGNORE);
32937 }
32938
32939 // ------------------------------------------------------------
32940 // ------------------------------------------------------------
32941 // ------------------------------------------------------------
32942 // Now unpackage the data
32943 // ------------------------------------------------------------
32944 // ------------------------------------------------------------
32945 // ------------------------------------------------------------
32946
32947 // Storage for the boundaries ids with vertices for non
32948 // deletion
32949 Vector<unsigned> recv_shd_bnd_id_for_non_deletion;
32950
32951 // Storage for chunk numbers of boundaries with vertices
32952 // for non deletion
32953 Vector<unsigned> recv_chunk_for_non_deletion;
32954
32955 // The number of vertices for nondeletion in the shared
32956 // boundaries
32957 Vector<unsigned> recv_number_vertices_non_deletion;
32958
32959 // Vertices marked for nondeletion in shared boundaries
32960 Vector<Vector<Vector<double>>> recv_vertices_for_non_deletion;
32961
32962 // Counter
32963 j = 0;
32964 for (unsigned i = 0; i < receive_count_unsigned_values; i += 3)
32965 {
32966 // Get the shared boundary id
32967 const unsigned recv_shd_bnd_id = flat_package_unsigned_recv[i];
32968 recv_shd_bnd_id_for_non_deletion.push_back(recv_shd_bnd_id);
32969 // Get the chunk number
32970 const unsigned recv_chunk = flat_package_unsigned_recv[i + 1];
32971 recv_chunk_for_non_deletion.push_back(recv_chunk);
32972 // Get the number of vertices for non deletion
32973 const unsigned recv_num_vertices = flat_package_unsigned_recv[i + 2];
32974 recv_number_vertices_non_deletion.push_back(recv_num_vertices);
32975
32976 // Create a temporal storage
32977 Vector<Vector<double>> temp_recv_vertices;
32978 // Now get the vertices
32979 for (unsigned h = 0; h < recv_num_vertices; h++)
32980 {
32981 Vector<double> tmp_vertex(2);
32982 tmp_vertex[0] = flat_package_double_recv[j++];
32983 tmp_vertex[1] = flat_package_double_recv[j++];
32984 // Add the vertex to the vector of vertices
32985 temp_recv_vertices.push_back(tmp_vertex);
32986 } // for (h<recv_num_vertices)
32987
32988 // Add the vertices to the vector of vertices
32989 recv_vertices_for_non_deletion.push_back(temp_recv_vertices);
32990
32991 } // for(i<receive_count_unsigned_values)
32992
32993 // ---------------------------------------------------------
32994 // ---------------------------------------------------------
32995 // ---------------------------------------------------------
32996 // Now add the vertices to the data structures to mark them
32997 // as non delete-able
32998 // ---------------------------------------------------------
32999 // ---------------------------------------------------------
33000 // ---------------------------------------------------------
33001
33002 // Get the number of shd boundaries that have vertices
33003 // marked for non deletion
33004 const unsigned n_recv_shd_bnd_id_for_non_deletion =
33005 recv_shd_bnd_id_for_non_deletion.size();
33006 // loop over the shared boundaries and add the data for non
33007 // deletion
33008 for (unsigned i = 0; i < n_recv_shd_bnd_id_for_non_deletion; i++)
33009 {
33010 // Get the shared boundary id.
33011 const unsigned shd_bnd_id = recv_shd_bnd_id_for_non_deletion[i];
33012 // Get the chunk number
33013 unsigned chunk = recv_chunk_for_non_deletion[i];
33014 // Increase and decrease the chunk number to avoid the
33015 // warning when compiling without PARANOID
33016 chunk++;
33017 chunk--;
33018
33019 // Get the number of vertices marked for non deletion
33020 const unsigned n_vertices = recv_number_vertices_non_deletion[i];
33021 // Add all the vertices
33022 for (unsigned h = 0; h < n_vertices; h++)
33023 {
33024 // Get the vertex
33025 Vector<double> vertex(2);
33026 vertex[0] = recv_vertices_for_non_deletion[i][h][0];
33027 vertex[1] = recv_vertices_for_non_deletion[i][h][1];
33028 // Add the vertex to the data structure for non
33029 // deletion
33030 // Boundary_chunk_connections_pt[shd_bnd_id][chunk].
33031 // insert(vertex);
33032 Boundary_connections_pt[shd_bnd_id].insert(vertex);
33033
33034 } // for (h<n_vertices)
33035
33036 } // for (i<n_recv_shd_bnd_id_for_non_deletion)
33037
33038 } // if (jproc != my_rank && n_shd_bnd_jproc > 0)
33039
33040 } // for (jproc < nproc)
33041 }
33042#endif // #ifdef OOMPH_HAS_MPI
33043
33044 //=========================================================================
33045 /// After unrefinement and refinement has taken place compute
33046 /// the new vertices numbers of the temporary representation of the
33047 // boundaries to connect.
33048 //=========================================================================
33049 template<class ELEMENT>
33051 Vector<TriangleMeshPolygon*>& tmp_outer_polygons_pt,
33052 Vector<TriangleMeshOpenCurve*>& tmp_open_curves_pt)
33053 {
33054 // Dummy storages
33055 Vector<TriangleMeshPolyLine*> dummy_resume_initial_connection_polyline_pt;
33056 Vector<TriangleMeshPolyLine*> dummy_resume_final_connection_polyline_pt;
33057
33058 // Clear the storage
33059 dummy_resume_initial_connection_polyline_pt.clear();
33060 dummy_resume_final_connection_polyline_pt.clear();
33061
33062 // Get the initial shared boundary id (to check whether the
33063 // polylines represent original or shared boundaries)
33064 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
33065
33066 // ------------------------------------------------------------------
33067 // This seems unnecesary since the outer polygons does not create
33068 // connections with other boundaries (the original ones)
33069 // ------------------------------------------------------------------
33070 // Unnecessary?
33071 // ------------------------------------------------------------------
33072
33073 // Loop over the temporary outer polygons create the connection
33074 // information of those boundaries marked to be connected at their
33075 // ends
33076
33077 // ------------------------------------------------------------------
33078 // Temporary outer polygons
33079 // ------------------------------------------------------------------
33080
33081 // Get the number of outer boundaries (closed boundaries)
33082 const unsigned n_outer_boundaries = tmp_outer_polygons_pt.size();
33083
33084 // Loop over the outer boundaries
33085 for (unsigned i = 0; i < n_outer_boundaries; i++)
33086 {
33087 // Get a temporary polygon representation
33088 TriangleMeshPolygon* tmp_polygon_pt = tmp_outer_polygons_pt[i];
33089 // Get the number of polylines associated to the current outer
33090 // boundary
33091 const unsigned n_polyline = tmp_polygon_pt->npolyline();
33092 // Loop over the polylines
33093 for (unsigned p = 0; p < n_polyline; p++)
33094 {
33095 // Get a temporary representation of the polyline
33096 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
33097
33098 // Get the boundary id
33099 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
33100
33101 // Is the boundary to connect a shared boundary
33102 if (bnd_id < init_shd_bnd_id)
33103 {
33104 // Restore the connections of the current polyline
33105 restore_polyline_connections_helper(
33106 tmp_polyline_pt,
33107 dummy_resume_initial_connection_polyline_pt,
33108 dummy_resume_final_connection_polyline_pt);
33109
33110 } // if (bnd_id < init_shd_bnd_id)
33111
33112 } // for (p < n_polyline)
33113
33114 } // for (i < n_outer_boundaries)
33115
33116 // ------------------------------------------------------------------
33117 // Unnecessary?
33118 // ------------------------------------------------------------------
33119
33120 // ------------------------------------------------------------------
33121 // Temporary open boundaries (nonclosed internal boundaries)
33122 // ------------------------------------------------------------------
33123
33124 // Get the number of internal boundaries (open boundaries)
33125 const unsigned n_open_boundaries = tmp_open_curves_pt.size();
33126
33127 // Loop over the internal open boundaries
33128 for (unsigned i = 0; i < n_open_boundaries; i++)
33129 {
33130 // Get a temporary representation for the open curve
33131 TriangleMeshOpenCurve* tmp_open_curve_pt = tmp_open_curves_pt[i];
33132
33133 // Get the number of curve sections associated to the current
33134 // internal open boundary
33135 const unsigned n_curve_section = tmp_open_curve_pt->ncurve_section();
33136
33137 // Loop over the curve section
33138 for (unsigned p = 0; p < n_curve_section; p++)
33139 {
33140 // Get a temporary representation of the curve section
33141 // (polyline)
33142 TriangleMeshPolyLine* tmp_polyline_pt =
33143 tmp_open_curve_pt->polyline_pt(p);
33144
33145 // Get the boundary id
33146 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
33147
33148 // Is the boundary to connect a shared boundary
33149 if (bnd_id < init_shd_bnd_id)
33150 {
33151 // Restore the connections of the current polyline
33152 restore_polyline_connections_helper(
33153 tmp_polyline_pt,
33154 dummy_resume_initial_connection_polyline_pt,
33155 dummy_resume_final_connection_polyline_pt);
33156
33157 } // if (bnd_id < init_shd_bnd_id)
33158
33159 } // for (p < n_curve_section)
33160
33161 } // for (i < n_open_boundaries)
33162 }
33163
33164 //=========================================================================
33165 /// After unrefinement and refinement has taken place compute
33166 /// the new vertices numbers of the boundaries to connect (in a
33167 /// distributed scheme it may be possible that the destination
33168 /// boundary does no longer exist, therefore the connection is
33169 /// suspended. It is not permanently deleted because if load balance
33170 /// takes place it may be possible that the boundary to connect be
33171 /// part of the new domain representation, so the connection would
33172 /// exist)
33173 //=========================================================================
33174 template<class ELEMENT>
33176 Vector<TriangleMeshPolyLine*>& resume_initial_connection_polyline_pt,
33177 Vector<TriangleMeshPolyLine*>& resume_final_connection_polyline_pt)
33178 {
33179 // Clear the storage
33180 resume_initial_connection_polyline_pt.clear();
33181 resume_final_connection_polyline_pt.clear();
33182
33183 // Loop over the boundaries in the domain (outer, internal -- closed
33184 // and open) and restore the connection information of those
33185 // boundaries marked to be connected at their ends
33186
33187 // ------------------------------------------------------------------
33188 // Outer boundaries
33189 // ------------------------------------------------------------------
33190
33191 // Get the number of outer boundaries (closed boundaries)
33192 const unsigned n_outer_boundaries = this->Outer_boundary_pt.size();
33193
33194 // Loop over the outer boundaries
33195 for (unsigned i = 0; i < n_outer_boundaries; i++)
33196 {
33197 // Get a temporary polygon representation
33198 TriangleMeshPolygon* tmp_polygon_pt = this->Outer_boundary_pt[i];
33199 // Get the number of polylines associated to the current outer
33200 // boundary
33201 const unsigned n_polyline = tmp_polygon_pt->npolyline();
33202 // Loop over the polylines
33203 for (unsigned p = 0; p < n_polyline; p++)
33204 {
33205 // Get a temporary representation of the polyline
33206 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
33207
33208 // Restore the connections of the current polyline
33209 restore_polyline_connections_helper(
33210 tmp_polyline_pt,
33211 resume_initial_connection_polyline_pt,
33212 resume_final_connection_polyline_pt);
33213
33214 } // for (p < n_polyline)
33215
33216 } // for (i < n_outer_boundaries)
33217
33218 // ------------------------------------------------------------------
33219 // Internal boundaries
33220 // ------------------------------------------------------------------
33221
33222 // Get the number of internal boundaries (closed boundaries)
33223 const unsigned n_internal_boundaries = this->Internal_polygon_pt.size();
33224
33225 // Loop over the internal boundaries
33226 for (unsigned i = 0; i < n_internal_boundaries; i++)
33227 {
33228 // Get a temporary polygon representation
33229 TriangleMeshPolygon* tmp_polygon_pt = this->Internal_polygon_pt[i];
33230 // Get the number of polylines associated to the current internal
33231 // boundary
33232 const unsigned n_polyline = tmp_polygon_pt->npolyline();
33233 // Loop over the polylines
33234 for (unsigned p = 0; p < n_polyline; p++)
33235 {
33236 // Get a temporary representation of the polyline
33237 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
33238
33239 // Restore the connections of the current polyline
33240 restore_polyline_connections_helper(
33241 tmp_polyline_pt,
33242 resume_initial_connection_polyline_pt,
33243 resume_final_connection_polyline_pt);
33244
33245 } // for (p < n_polyline)
33246
33247 } // for (i < n_internal_boundaries)
33248
33249 // ------------------------------------------------------------------
33250 // Open boundaries (nonclosed internal boundaries)
33251 // ------------------------------------------------------------------
33252
33253 // Get the number of internal boundaries (open boundaries)
33254 const unsigned n_open_boundaries = this->Internal_open_curve_pt.size();
33255
33256 // Loop over the internal open boundaries
33257 for (unsigned i = 0; i < n_open_boundaries; i++)
33258 {
33259 // Get a temporary representation for the open curve
33260 TriangleMeshOpenCurve* tmp_open_curve_pt =
33261 this->Internal_open_curve_pt[i];
33262
33263 // Get the number of curve sections associated to the current
33264 // internal open boundary
33265 const unsigned n_curve_section = tmp_open_curve_pt->ncurve_section();
33266
33267 // Loop over the curve section
33268 for (unsigned p = 0; p < n_curve_section; p++)
33269 {
33270 // Get a temporary representation of the curve section
33271 // (polyline)
33272 TriangleMeshPolyLine* tmp_polyline_pt =
33273 tmp_open_curve_pt->polyline_pt(p);
33274
33275 // Restore the connections of the current polyline
33276 restore_polyline_connections_helper(
33277 tmp_polyline_pt,
33278 resume_initial_connection_polyline_pt,
33279 resume_final_connection_polyline_pt);
33280
33281 } // for (p < n_curve_section)
33282
33283 } // for (i < n_open_boundaries)
33284 }
33285
33286 //=========================================================================
33287 /// Restore the connections of the specific polyline
33288 /// The vertices numbering on the destination boundaries may have
33289 /// change because of (un)refinement in the destination boundaries.
33290 /// Also deals with connection that do not longer exist because the
33291 /// destination boundary does no longer exist because of the distribution
33292 /// process
33293 //=========================================================================
33294 template<class ELEMENT>
33296 TriangleMeshPolyLine* polyline_pt,
33297 Vector<TriangleMeshPolyLine*>& resume_initial_connection_polyline_pt,
33298 Vector<TriangleMeshPolyLine*>& resume_final_connection_polyline_pt)
33299 {
33300 // If the polyline is connected at any of its ends compute the new
33301 // vertex number on the destination boundary
33302
33303 // ------------------------------------------------------------------
33304 // Is the initial vertex connected?
33305 if (polyline_pt->is_initial_vertex_connected())
33306 {
33307 // The pointer to the boundary to connect
33308 TriangleMeshPolyLine* poly_to_connect_pt = 0;
33309
33310 // Get the boundary id of the destination/connected boundary
33311 const unsigned dst_bnd_id_initial =
33312 polyline_pt->initial_vertex_connected_bnd_id();
33313
33314 // Get the initial vertex on the current boundary
33315 Vector<double> src_vertex_coordinates_initial =
33316 polyline_pt->vertex_coordinate(0);
33317
33318#ifdef PARANOID
33319 // Is the mesh distributed?
33320#ifdef OOMPH_HAS_MPI
33321 if (this->is_mesh_distributed())
33322 {
33323 // Get the initial shared boundary id
33324 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
33325 // Is the boundary to connect a shared boundary
33326 if (dst_bnd_id_initial >= init_shd_bnd_id)
33327 {
33328 // Get the current polyline original boundary id
33329 const unsigned bnd_id = polyline_pt->boundary_id();
33330 std::ostringstream error_message;
33331 error_message
33332 << "INITIAL VERTEX CONNECTION\n"
33333 << "The current original boundary is trying to connect to a\n"
33334 << "shared boundary, this is not allowed. In this case the\n"
33335 << "shared boundary should be the one that connects with the\n"
33336 << "original boundary\n"
33337 << "The current original boundary (" << bnd_id << ") is marked\n"
33338 << "to have a connection at the\nINITIAL vertex ("
33339 << src_vertex_coordinates_initial[0] << ","
33340 << src_vertex_coordinates_initial[1] << ")\n"
33341 << "with the shared boundary (" << dst_bnd_id_initial << ")\n"
33342 << "This is the list of vertices on the shared destination "
33343 "boundary\n";
33344 // Get the pointer to the associated polyline by using the
33345 // boundary id
33346 TriangleMeshPolyLine* dst_polyline =
33347 this->boundary_polyline_pt(dst_bnd_id_initial);
33348 // The number of vertices on the destination boundary
33349 const unsigned n_vertex_dst_boundary = dst_polyline->nvertex();
33350 // Loop over the vertices print them
33351 for (unsigned i = 0; i < n_vertex_dst_boundary; i++)
33352 {
33353 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
33354 error_message << "Vertex#(i): (" << current_vertex[0] << ", "
33355 << current_vertex[1] << ")\n";
33356 }
33357 throw OomphLibError(
33358 error_message.str(),
33359 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33360 OOMPH_EXCEPTION_LOCATION);
33361 } // if (dst_bnd_id_initial >= init_shd_bnd_id)
33362
33363 } // if (this->is_mesh_distributed())
33364#endif // #ifdef OOMPH_HAS_MPI
33365
33366#endif // #ifdef PARANOID
33367
33368 // Flag to indicate if the vertex was found on the destination
33369 // boundary
33370 bool found_vertex_on_dst_boundary_initial = false;
33371
33372 // Flag that stores the chunk number to connect (only used in
33373 // distributed meshes)
33374 unsigned sub_poly_to_connect = 0;
33375
33376 // Store the vertex number on the destination boundary
33377 unsigned n_vertex_connection_initial = 0;
33378
33379 // Flags only used in a distributed mesh
33380 // ----------------------------------------
33381 // Flag to indicate we are trying to connect to an split boundary
33382 bool connecting_to_an_split_boundary = false;
33383
33384 // Flag to indicate we are trying to connecto to an internal
33385 // boundary that is overlaped by a shared boundary)
33386 bool connecting_to_an_overlaped_boundary = false;
33387
33388#ifdef OOMPH_HAS_MPI
33389 if (this->is_mesh_distributed())
33390 {
33391 // We can only connect to an original boundary, check if the
33392 // boundary was splitted during the distribution process to
33393 // consider all the chunks (sub-polylines) of the boundary
33394 if (this->boundary_was_splitted(dst_bnd_id_initial))
33395 {
33396 connecting_to_an_split_boundary = true;
33397 } // if (this->boundary_was_splitted(dst_bnd_id_initial))
33398
33399 // Check if the destination boundary, or any of its chunks is
33400 // marked to be overlapped by a shared boundary, if that is the
33401 // case we can only connect to the chunks that are not
33402 // overlapped by shared boundaries (the shared boundaries are in
33403 // charge of generating the connections with original boundaries
33404 // and with themselves)
33405 if (connecting_to_an_split_boundary)
33406 {
33407 // Get the number of chucks that represent the destination
33408 // boundary
33409 const unsigned n_sub_poly =
33410 this->nboundary_subpolylines(dst_bnd_id_initial);
33411 // Now loop over the chunks of the destination boundary and if
33412 // any of them is marked to be overlaped by a shared boundary
33413 // then set the flag and break the loop
33414 for (unsigned ii = 0; ii < n_sub_poly; ii++)
33415 {
33416 if (this->boundary_marked_as_shared_boundary(dst_bnd_id_initial,
33417 ii))
33418 {
33419 // Mark the boundary as being overlaped by a shared
33420 // boundary
33421 connecting_to_an_overlaped_boundary = true;
33422 // Break, no need to look for more overlapings
33423 break;
33424 } // if (boundary_marked_as_shared_boundary(...))
33425 } // for (ii < n_sub_poly)
33426 } // if (connecting_to_an_split_boundary)
33427 else
33428 {
33429 // If not connecting to an split boundary then check if the
33430 // whole destination boundary is overlaped by an internal
33431 // boundary
33432 if (this->boundary_marked_as_shared_boundary(dst_bnd_id_initial, 0))
33433 {
33434 // Mark the boundary as being overlaped by a shared boundary
33435 connecting_to_an_overlaped_boundary = true;
33436 } // if (boundary_marked_as_shared_boundary(...))
33437 } // else if (connecting_to_an_split_boundary)
33438
33439 } // if (this->is_mesh_distributed())
33440
33441#endif // #ifdef OOMPH_HAS_MPI
33442
33443 // If we are connecting neither to an split boundary nor an
33444 // overlaped boundary then get the pointer to the original
33445 // boundary
33446 if (!(connecting_to_an_split_boundary ||
33447 connecting_to_an_overlaped_boundary))
33448 {
33449 // Get the polyline pointer representing the destination
33450 // boundary
33451 poly_to_connect_pt = this->boundary_polyline_pt(dst_bnd_id_initial);
33452 } // else if (NOT split, NOT overlaped)
33453
33454 // Now look for the vertex number on the destination boundary(ies)
33455 // -- in case that the boundary was split ---
33456
33457 // Do not check for same orientation, that was previously worked
33458 // by interchanging the connections boundaries (if necessary)
33459
33460 // If the boundary was not split then ...
33461 if (!connecting_to_an_split_boundary)
33462 {
33463 // ... check if the boundary is marked to be overlaped by
33464 // a shared boundary
33465 if (!connecting_to_an_overlaped_boundary)
33466 {
33467 // If that is not the case then we can safely look for the
33468 // vertex number on the destination boundary
33469 found_vertex_on_dst_boundary_initial =
33470 this->get_connected_vertex_number_on_destination_polyline(
33471 poly_to_connect_pt,
33472 src_vertex_coordinates_initial,
33473 n_vertex_connection_initial);
33474
33475 } // if (!connecting_to_an_overlaped_boundary)
33476 else
33477 {
33478 // If the whole boundary is marked to be overlaped by a shared
33479 // boundary then do nothing, the shared boundaries are already
33480 // in charge of performing the connection (it will be required
33481 // to disabled the connection) with the original boundary
33482
33483 } // else if (!connecting_to_an_overlaped_boundary)
33484
33485 } // if (!connecting_to_an_split_boundary)
33486#ifdef OOMPH_HAS_MPI
33487 else
33488 {
33489 // If the boundary was split then we need to look for the vertex
33490 // in the sub-polylines
33491
33492 // Get the sub-polylines vector
33493 Vector<TriangleMeshPolyLine*> tmp_vector_subpolylines =
33494 this->boundary_subpolylines(dst_bnd_id_initial);
33495
33496 // Get the number of sub-polylines
33497 const unsigned nsub_poly = tmp_vector_subpolylines.size();
33498#ifdef PARANOID
33499 if (nsub_poly <= 1)
33500 {
33501 std::ostringstream error_message;
33502 error_message << "The boundary (" << dst_bnd_id_initial << ") was "
33503 << "marked to be splitted but\n"
33504 << "there are only (" << nsub_poly << ") polylines to "
33505 << "represent it.\n";
33506 throw OomphLibError(
33507 error_message.str(),
33508 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33509 OOMPH_EXCEPTION_LOCATION);
33510 } // if (nsub_poly <= 1)
33511#endif
33512 // We need to check if the boundary is marked to be overlaped by
33513 // a shared boundary, if that is the case we need to check for
33514 // each indivual subpolyline, and for those overlaped by a
33515 // shared polyline do nothing, the shared polylines have already
33516 // deal with these connections
33517
33518 // ... check if the boundary is marked to be overlaped by
33519 // a shared boundary
33520 if (!connecting_to_an_overlaped_boundary)
33521 {
33522 // The boundary is not overlapped by shared boundaries, we can
33523 // work without checking the subpolylines individually (non of
33524 // them are overlapped by a shared boundary)
33525
33526 // Look for the vertex number to connect on each of the
33527 // subpolyines
33528 for (unsigned isub = 0; isub < nsub_poly; isub++)
33529 {
33530 // Assign the pointer to the sub-polyline
33531 poly_to_connect_pt = tmp_vector_subpolylines[isub];
33532 // Search for the vertex in the current sub-polyline
33533 found_vertex_on_dst_boundary_initial =
33534 this->get_connected_vertex_number_on_destination_polyline(
33535 poly_to_connect_pt,
33536 src_vertex_coordinates_initial,
33537 n_vertex_connection_initial);
33538
33539 // If we have found the vertex to connect then break the
33540 // loop
33541 if (found_vertex_on_dst_boundary_initial)
33542 {
33543 // But first save the subpoly number (chunk), that will be
33544 // used to perform the connection
33545 sub_poly_to_connect = isub;
33546 break;
33547 } // if (found_vertex_on_dst_boundary_initial)
33548
33549 } // for (isub < nsub_poly)
33550
33551 } // if (!connecting_to_an_overlaped_boundary)
33552 else
33553 {
33554 // If connecting to an overlapped boundary then we ignore the
33555 // subpolylines overlapped by shared boundaries and only look
33556 // on the sub-polylines that are not marked as being overlaped
33557 // by shared boundaries
33558
33559 // Look for the vertex number to connect on each of the
33560 // subpolyines
33561 for (unsigned isub = 0; isub < nsub_poly; isub++)
33562 {
33563 // Only work with those sub-polylines that are not overlaped
33564 // by shared boundaries
33565 if (!this->boundary_marked_as_shared_boundary(dst_bnd_id_initial,
33566 isub))
33567 {
33568 // Assign the pointer to the sub-polyline
33569 poly_to_connect_pt = tmp_vector_subpolylines[isub];
33570
33571 // Search for the vertex in the current sub-polyline
33572 found_vertex_on_dst_boundary_initial =
33573 this->get_connected_vertex_number_on_destination_polyline(
33574 poly_to_connect_pt,
33575 src_vertex_coordinates_initial,
33576 n_vertex_connection_initial);
33577
33578 // Was the vertex found?
33579 if (found_vertex_on_dst_boundary_initial)
33580 {
33581 // But first save the subpoly number (chunk), that will
33582 // be used to perform the connection
33583 sub_poly_to_connect = isub;
33584 break;
33585 } // if (found_vertex_on_dst_boundary_initial)
33586
33587 } // if (not overlaped by shared boundary)
33588
33589 } // for (isub < nsub_poly)
33590
33591 } // else if (!connecting_to_an_overlaped_boundary)
33592
33593 } // else if (!connecting_to_an_split_boundary)
33594#endif // #ifdef OOMPH_HAS_MPI
33595
33596 // If not found it may be that the connection information is
33597 // inverted
33598 if (!found_vertex_on_dst_boundary_initial)
33599 {
33600 // Is the mesh distributed?
33601#ifdef OOMPH_HAS_MPI
33602 if (this->is_mesh_distributed())
33603 {
33604 // If the mesh is distributed and the vertex number was not
33605 // found, that means that the boundary (or vertex) to connect
33606 // in the destination boundary is not in the current
33607 // processor. In that case suspend the connection
33608 polyline_pt->suspend_initial_vertex_connected();
33609 // Add the polyline to the vector of polylines whose
33610 // connection will be resumed at the end of the adaptation
33611 // process
33612 resume_initial_connection_polyline_pt.push_back(polyline_pt);
33613 // The shared boundaries are marked to connect to the initial
33614 // vertex of the polyline (remember that a shared boundary
33615 // stops adding nodes when it finds a node on an original
33616 // boundary) -- The initial vertex is now a base node
33617 }
33618 else
33619#endif // #ifdef OOMPH_HAS_MPI
33620 {
33621#ifdef PARANOID
33622 // If not found then there is a problem with the vertices
33623 // Get the associated boundary id of the current polyline
33624 const unsigned bnd_id = polyline_pt->boundary_id();
33625 std::ostringstream error_message;
33626 error_message
33627 << "INITIAL VERTEX CONNECTION\n"
33628 << "It was not possible to find the associated "
33629 << "vertex number on the destination boundary\n"
33630 << "The current boundary (" << bnd_id << ") is marked to have"
33631 << "a connection at the\nINITIAL vertex ("
33632 << src_vertex_coordinates_initial[0] << ","
33633 << src_vertex_coordinates_initial[1] << ")\n"
33634 << "with boundary (" << dst_bnd_id_initial << ")\n"
33635 << "This is the list of vertices on the destination boundary\n";
33636 // Get the pointer to the associated polyline by using the
33637 // boundary id
33638 TriangleMeshPolyLine* dst_polyline =
33639 this->boundary_polyline_pt(dst_bnd_id_initial);
33640 // The number of vertices on the destination boundary
33641 const unsigned n_vertex_dst_boundary = dst_polyline->nvertex();
33642 // Loop over the vertices print them
33643 for (unsigned i = 0; i < n_vertex_dst_boundary; i++)
33644 {
33645 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
33646 error_message << "Vertex#(i): (" << current_vertex[0] << ", "
33647 << current_vertex[1] << ")\n";
33648 }
33649 throw OomphLibError(
33650 error_message.str(),
33651 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33652 OOMPH_EXCEPTION_LOCATION);
33653#endif
33654
33655 } // else if (this->is_mesh_distributed())
33656
33657 } // if (!found_vertex_on_dst_boundary_initial)
33658 else
33659 {
33660 // Set the vertex number on the destination boundary
33661 polyline_pt->initial_vertex_connected_n_vertex() =
33662 n_vertex_connection_initial;
33663
33664 // Set the chunk number on the destination boundary
33665 polyline_pt->initial_vertex_connected_n_chunk() = sub_poly_to_connect;
33666
33667 } // else if (!found_vertex_on_dst_boundary_initial)
33668
33669 } // if (polyline_pt->is_initial_vertex_connected())
33670
33671 // ------------------------------------------------------------------
33672 // Is the final vertex connected?
33673 if (polyline_pt->is_final_vertex_connected())
33674 {
33675 // The pointer to the boundary to connect
33676 TriangleMeshPolyLine* poly_to_connect_pt = 0;
33677
33678 // Get the boundary id of the destination/connected boundary
33679 const unsigned dst_bnd_id_final =
33680 polyline_pt->final_vertex_connected_bnd_id();
33681
33682 // Get the final vertex on the current boundary
33683 const unsigned tmp_n_vertices = polyline_pt->nvertex();
33684 Vector<double> src_vertex_coordinates_final =
33685 polyline_pt->vertex_coordinate(tmp_n_vertices - 1);
33686
33687
33688#ifdef PARANOID
33689 // Is the mesh distributed?
33690#ifdef OOMPH_HAS_MPI
33691 if (this->is_mesh_distributed())
33692 {
33693 // Get the initial shared boundary id
33694 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
33695 // Is the boundary to connect a shared boundary
33696 if (dst_bnd_id_final >= init_shd_bnd_id)
33697 {
33698 // Get the current polyline original boundary id
33699 const unsigned bnd_id = polyline_pt->boundary_id();
33700 std::ostringstream error_message;
33701 error_message
33702 << "FINAL VERTEX CONNECTION\n"
33703 << "The current original boundary is trying to connect to a\n"
33704 << "shared boundary, this is not allowed. In this case the\n"
33705 << "shared boundary should be the one that connects with the\n"
33706 << "original boundary\n"
33707 << "The current boundary (" << bnd_id << ") is marked to have "
33708 << "a connection at the\nFINAL vertex ("
33709 << src_vertex_coordinates_final[0] << ","
33710 << src_vertex_coordinates_final[1] << ")\n"
33711 << "with boundary (" << dst_bnd_id_final << ")\n"
33712 << "This is the list of vertices on the destination boundary\n";
33713 // Get the pointer to the associated polyline by using the
33714 // boundary id
33715 TriangleMeshPolyLine* dst_polyline =
33716 this->boundary_polyline_pt(dst_bnd_id_final);
33717 // The number of vertices on the destination boundary
33718 const unsigned n_vertex_dst_boundary = dst_polyline->nvertex();
33719 // Loop over the vertices print them
33720 for (unsigned i = 0; i < n_vertex_dst_boundary; i++)
33721 {
33722 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
33723 error_message << "Vertex#(" << i << "): (" << current_vertex[0]
33724 << ", " << current_vertex[1] << ")\n";
33725 }
33726 throw OomphLibError(
33727 error_message.str(),
33728 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33729 OOMPH_EXCEPTION_LOCATION);
33730 } // if (dst_bnd_id_initial >= init_shd_bnd_id)
33731
33732 } // if (this->is_mesh_distributed())
33733#endif // #ifdef OOMPH_HAS_MPI
33734
33735#endif // #ifdef PARANOID
33736
33737 // Flag to indicate if the vertex was found on the destination
33738 // boundary
33739 bool found_vertex_on_dst_boundary_final = false;
33740
33741 // Flag that stores the chunk number to connect (only used in
33742 // distributed meshes)
33743 unsigned sub_poly_to_connect = 0;
33744
33745 // Store the vertex number on the destination boundary
33746 unsigned n_vertex_connection_final = 0;
33747
33748 // Flags only used in a distributed mesh
33749 // ----------------------------------------
33750 // Flag to indicate we are trying to connect to an split boundary
33751 bool connecting_to_an_split_boundary = false;
33752
33753 // Flag to indicate we are trying to connecto to an internal
33754 // boundary that is overlaped by a shared boundary)
33755 bool connecting_to_an_overlaped_boundary = false;
33756
33757#ifdef OOMPH_HAS_MPI
33758 if (this->is_mesh_distributed())
33759 {
33760 // We can only connect to an original boundary, check if the
33761 // boundary was splitted during the distribution process to
33762 // consider all the chunks (sub-polylines) of the boundary
33763 if (this->boundary_was_splitted(dst_bnd_id_final))
33764 {
33765 connecting_to_an_split_boundary = true;
33766 } // if (this->boundary_was_splitted(dst_bnd_id_final))
33767
33768 // Check if the destination boundary, or any of its chunks is
33769 // marked to be overlapped by a shared boundary, if that is the
33770 // case we can only connect to the chunks that are not
33771 // overlapped by shared boundaries (the shared boundaries are in
33772 // charge of generating the connections with original boundaries
33773 // and with themselves)
33774 if (connecting_to_an_split_boundary)
33775 {
33776 // Get the number of chucks that represent the destination
33777 // boundary
33778 const unsigned n_sub_poly =
33779 this->nboundary_subpolylines(dst_bnd_id_final);
33780 // Now loop over the chunks of the destination boundary and if
33781 // any of them is marked to be overlaped by a shared boundary
33782 // then set the flag and break the loop
33783 for (unsigned ii = 0; ii < n_sub_poly; ii++)
33784 {
33785 if (this->boundary_marked_as_shared_boundary(dst_bnd_id_final, ii))
33786 {
33787 // Mark the boundary as being overlaped by a shared
33788 // boundary
33789 connecting_to_an_overlaped_boundary = true;
33790 // Break, no need to look for more overlapings
33791 break;
33792 } // if (boundary_marked_as_shared_boundary(...))
33793 } // for (ii < n_sub_poly)
33794 } // if (connecting_to_an_split_boundary)
33795 else
33796 {
33797 // If not connecting to an split boundary then check if the
33798 // whole destination boundary is overlaped by an internal
33799 // boundary
33800 if (this->boundary_marked_as_shared_boundary(dst_bnd_id_final, 0))
33801 {
33802 // Mark the boundary as being overlaped by a shared boundary
33803 connecting_to_an_overlaped_boundary = true;
33804 } // if (boundary_marked_as_shared_boundary(...))
33805 } // else if (connecting_to_an_split_boundary)
33806
33807 } // if (this->is_mesh_distributed())
33808
33809#endif // #ifdef OOMPH_HAS_MPI
33810
33811 // If we are connecting neither to an split boundary nor an
33812 // overlaped boundary then get the pointer to the original
33813 // boundary
33814 if (!(connecting_to_an_split_boundary ||
33815 connecting_to_an_overlaped_boundary))
33816 {
33817 // Get the polyline pointer representing the destination
33818 // boundary
33819 poly_to_connect_pt = this->boundary_polyline_pt(dst_bnd_id_final);
33820 } // else if (NOT split, NOT overlaped)
33821
33822 // Now look for the vertex number on the destination boundary(ies)
33823 // -- in case that the boundary was split ---
33824
33825 // Do not check for same orientation, that was previously worked
33826 // by interchanging the connections boundaries (if necessary)
33827
33828 // If the boundary was not split then ...
33829 if (!connecting_to_an_split_boundary)
33830 {
33831 // ... check if the boundary is marked to be overlaped by
33832 // a shared boundary
33833 if (!connecting_to_an_overlaped_boundary)
33834 {
33835 // If that is not the case then we can safely look for the
33836 // vertex number on the destination boundary
33837 found_vertex_on_dst_boundary_final =
33838 this->get_connected_vertex_number_on_destination_polyline(
33839 poly_to_connect_pt,
33840 src_vertex_coordinates_final,
33841 n_vertex_connection_final);
33842
33843 } // if (!connecting_to_an_overlaped_boundary)
33844 else
33845 {
33846 // If the whole boundary is marked to be overlaped by a shared
33847 // boundary then do nothing, the shared boundaries are already
33848 // in charge of performing the connection (it will be required
33849 // to disabled the connection) with the original boundary
33850
33851 } // else if (!connecting_to_an_overlaped_boundary)
33852
33853 } // if (!connecting_to_an_split_boundary)
33854#ifdef OOMPH_HAS_MPI
33855 else
33856 {
33857 // If the boundary was split then we need to look for the vertex
33858 // in the sub-polylines
33859
33860 // Get the sub-polylines vector
33861 Vector<TriangleMeshPolyLine*> tmp_vector_subpolylines =
33862 this->boundary_subpolylines(dst_bnd_id_final);
33863
33864 // Get the number of sub-polylines
33865 const unsigned nsub_poly = tmp_vector_subpolylines.size();
33866#ifdef PARANOID
33867 if (nsub_poly <= 1)
33868 {
33869 std::ostringstream error_message;
33870 error_message << "The boundary (" << dst_bnd_id_final << ") was "
33871 << "marked to be splitted but\n"
33872 << "there are only (" << nsub_poly << ") polylines to "
33873 << "represent it.\n";
33874 throw OomphLibError(
33875 error_message.str(),
33876 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33877 OOMPH_EXCEPTION_LOCATION);
33878 } // if (nsub_poly <= 1)
33879#endif
33880 // We need to check if the boundary is marked to be overlaped by
33881 // a shared boundary, if that is the case we need to check for
33882 // each indivual subpolyline, and for those overlaped by a
33883 // shared polyline do nothing, the shared polylines have already
33884 // deal with these connections
33885
33886 // ... check if the boundary is marked to be overlaped by
33887 // a shared boundary
33888 if (!connecting_to_an_overlaped_boundary)
33889 {
33890 // The boundary is not overlapped by shared boundaries, we can
33891 // work without checking the subpolylines individually (non of
33892 // them are overlapped by a shared boundary)
33893
33894 // Look for the vertex number to connect on each of the
33895 // subpolyines
33896 for (unsigned isub = 0; isub < nsub_poly; isub++)
33897 {
33898 // Assign the pointer to the sub-polyline
33899 poly_to_connect_pt = tmp_vector_subpolylines[isub];
33900 // Search for the vertex in the current sub-polyline
33901 found_vertex_on_dst_boundary_final =
33902 this->get_connected_vertex_number_on_destination_polyline(
33903 poly_to_connect_pt,
33904 src_vertex_coordinates_final,
33905 n_vertex_connection_final);
33906
33907 // If we have found the vertex to connect then break the
33908 // loop
33909 if (found_vertex_on_dst_boundary_final)
33910 {
33911 // But first save the subpoly number (chunk), that will be
33912 // used to perform the connection
33913 sub_poly_to_connect = isub;
33914 break;
33915 } // if (found_vertex_on_dst_boundary_initial)
33916
33917 } // for (isub < nsub_poly)
33918
33919 } // if (!connecting_to_an_overlaped_boundary)
33920 else
33921 {
33922 // If connecting to an overlapped boundary then we ignore the
33923 // subpolylines overlapped by shared boundaries and only look
33924 // on the sub-polylines that are not marked as being overlaped
33925 // by shared boundaries
33926
33927 // Look for the vertex number to connect on each of the
33928 // subpolyines
33929 for (unsigned isub = 0; isub < nsub_poly; isub++)
33930 {
33931 // Only work with those sub-polylines that are not overlaped
33932 // by shared boundaries
33933 if (!this->boundary_marked_as_shared_boundary(dst_bnd_id_final,
33934 isub))
33935 {
33936 // Assign the pointer to the sub-polyline
33937 poly_to_connect_pt = tmp_vector_subpolylines[isub];
33938
33939 // Search for the vertex in the current sub-polyline
33940 found_vertex_on_dst_boundary_final =
33941 this->get_connected_vertex_number_on_destination_polyline(
33942 poly_to_connect_pt,
33943 src_vertex_coordinates_final,
33944 n_vertex_connection_final);
33945
33946 // Was the vertex found?
33947 if (found_vertex_on_dst_boundary_final)
33948 {
33949 // But first save the subpoly number (chunk), that will
33950 // be used to perform the connection
33951 sub_poly_to_connect = isub;
33952 break;
33953 } // if (found_vertex_on_dst_boundary_final)
33954
33955 } // if (not overlaped by shared boundary)
33956
33957 } // for (isub < nsub_poly)
33958
33959 } // else if (!connecting_to_an_overlaped_boundary)
33960
33961 } // else if (!connecting_to_an_split_boundary)
33962#endif // #ifdef OOMPH_HAS_MPI
33963
33964 // If not found it may be that the connection information is
33965 // inverted
33966 if (!found_vertex_on_dst_boundary_final)
33967 {
33968 // Is the mesh distributed?
33969#ifdef OOMPH_HAS_MPI
33970 if (this->is_mesh_distributed())
33971 {
33972 // If the mesh is distributed and the vertex number was not
33973 // found, that means that the boundary (or vertex) to connect
33974 // in the destination boundary is not in the current
33975 // processor. In that suspend the connection
33976 polyline_pt->suspend_final_vertex_connected();
33977 // Add the polyline to the vector of polylines whose
33978 // connection will be resumed at the end of the adaptation
33979 // process
33980 resume_final_connection_polyline_pt.push_back(polyline_pt);
33981 // The shared boundaries are marked to connect to the final
33982 // vertex of the polyline (remember that a shared boundary
33983 // stops adding nodes when it finds a node on an original
33984 // boundary) -- The final vertex is now a base node
33985 } // if (this->is_mesh_distributed())
33986 else
33987#endif // #ifdef OOMPH_HAS_MPI
33988 {
33989#ifdef PARANOID
33990 // If not found then there is a problem with the vertices
33991 // Get the associated boundary id of the current polyline
33992 const unsigned bnd_id = polyline_pt->boundary_id();
33993 std::ostringstream error_message;
33994 error_message
33995 << "FINAL VERTEX CONNECTION\n"
33996 << "It was not possible to find the associated "
33997 << "vertex number on the destination boundary\n"
33998 << "The current boundary (" << bnd_id << ") is marked to have "
33999 << "a connection at the\nFINAL vertex ("
34000 << src_vertex_coordinates_final[0] << ","
34001 << src_vertex_coordinates_final[1] << ")\n"
34002 << "with boundary (" << dst_bnd_id_final << ")\n"
34003 << "This is the list of vertices on the destination boundary\n";
34004 // Get the pointer to the associated polyline by using the
34005 // boundary id
34006 TriangleMeshPolyLine* dst_polyline =
34007 this->boundary_polyline_pt(dst_bnd_id_final);
34008 // The number of vertices on the destination boundary
34009 const unsigned n_vertex_dst_boundary = dst_polyline->nvertex();
34010 // Loop over the vertices print them
34011 for (unsigned i = 0; i < n_vertex_dst_boundary; i++)
34012 {
34013 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
34014 error_message << "Vertex#(" << i << "): (" << current_vertex[0]
34015 << ", " << current_vertex[1] << ")\n";
34016 }
34017 throw OomphLibError(
34018 error_message.str(),
34019 "RefineableTriangleMesh::restore_polyline_connections_helper()",
34020 OOMPH_EXCEPTION_LOCATION);
34021#endif
34022 } // else if (this->is_mesh_distributed())
34023
34024 } // if (!found_vertex_on_dst_boundary_final)
34025 else
34026 {
34027 // Set the vertex number on the destination boundary
34028 polyline_pt->final_vertex_connected_n_vertex() =
34029 n_vertex_connection_final;
34030
34031 // Set the chunk number on the destination boundary
34032 polyline_pt->final_vertex_connected_n_chunk() = sub_poly_to_connect;
34033
34034 } // else if (!found_vertex_on_dst_boundary_final)
34035
34036 } // if (polyline_pt->is_final_vertex_connected())
34037 }
34038
34039 //=========================================================================
34040 /// Resume the boundary connections that may have been
34041 /// suspended because the destination boundary is no part of the
34042 /// domain. The connections are no permanently suspended because if
34043 /// load balance takes place the destination boundary may be part of
34044 /// the new domain representation therefore the connection would
34045 /// exist
34046 //=========================================================================
34047 template<class ELEMENT>
34049 Vector<TriangleMeshPolyLine*>& resume_initial_connection_polyline_pt,
34050 Vector<TriangleMeshPolyLine*>& resume_final_connection_polyline_pt)
34051 {
34052 // Get the number of polylines that require to resume the connection
34053 // at the initial vertex
34054 const unsigned n_initial_poly =
34055 resume_initial_connection_polyline_pt.size();
34056 // Loop over the polylines that require to resume the connection
34057 // at the initial vertex
34058 for (unsigned p = 0; p < n_initial_poly; p++)
34059 {
34060 // Get the polyline
34061 TriangleMeshPolyLine* tmp_poly_pt =
34062 resume_initial_connection_polyline_pt[p];
34063 // Resume the connection with the initial vertex
34064 tmp_poly_pt->resume_initial_vertex_connected();
34065 } // for (p < n_initial_poly)
34066
34067 // Get the number of polylines that require to resume the connection
34068 // at the final vertex
34069 const unsigned n_final_poly = resume_final_connection_polyline_pt.size();
34070 // Loop over the polylines that require to resume the connection at
34071 // the final vertex
34072 for (unsigned p = 0; p < n_final_poly; p++)
34073 {
34074 // Get the polyline
34075 TriangleMeshPolyLine* tmp_poly_pt =
34076 resume_final_connection_polyline_pt[p];
34077 // Resume the connection with the final vertex
34078 tmp_poly_pt->resume_final_vertex_connected();
34079 } // for (p < n_final_poly)
34080
34081 // Clear the storage
34082 resume_initial_connection_polyline_pt.clear();
34083 resume_final_connection_polyline_pt.clear();
34084 }
34085
34086 //=========================================================================
34087 /// Gets the associated vertex number according to the vertex
34088 /// coordinates on the destination boundary
34089 //=========================================================================
34090 template<class ELEMENT>
34093 Vector<double>& vertex_coordinates,
34094 const unsigned& dst_bnd_id,
34095 unsigned& vertex_number)
34096 {
34097 bool found_associated_vertex_number = false;
34098
34099 // Get the pointer to the associated polyline by using the boundary id
34100 TriangleMeshPolyLine* dst_polyline = this->boundary_polyline_pt(dst_bnd_id);
34101
34102 const unsigned n_vertices = dst_polyline->nvertex();
34103
34104 // Loop over the vertices and return the closest vertex
34105 // to the given vertex coordinates
34106 for (unsigned i = 0; i < n_vertices; i++)
34107 {
34108 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
34109
34110 double error = (vertex_coordinates[0] - current_vertex[0]) *
34111 (vertex_coordinates[0] - current_vertex[0]) +
34112 (vertex_coordinates[1] - current_vertex[1]) *
34113 (vertex_coordinates[1] - current_vertex[1]);
34114
34115 error = sqrt(error);
34116
34118 {
34119 vertex_number = i;
34120 found_associated_vertex_number = true;
34121 break;
34122 }
34123 }
34124
34125 return found_associated_vertex_number;
34126 }
34127
34128 //=========================================================================
34129 /// Helper function that updates the input polygon's PSLG
34130 /// by using the end-points of elements from FaceMesh(es) that are
34131 /// constructed for the boundaries associated with the segments of the
34132 /// polygon. Optional boolean is used to run it as test only (if
34133 /// true is specified as input) in which case polygon isn't actually
34134 /// modified. Returned boolean indicates if polygon was (or would have
34135 /// been -- if called with check_only=false) changed.
34136 //=========================================================================
34137 template<class ELEMENT>
34139 TriangleMeshPolygon* polygon_pt, const bool& check_only)
34140 {
34141#ifdef PARANOID
34142 // If the mesh is marked as distributed this method can not be
34143 // called since there is no guarantee of creating (distributed)
34144 // meshes that match in the number and position of nodes at their
34145 // shared boundaries. The only exececption is when called with
34146 // check_only=true, since no boundary updating is performed
34147 if (this->is_mesh_distributed() && !check_only)
34148 {
34149 std::stringstream error_message;
34150 error_message
34151 << "The updating of polygons of a distributed mesh can ONLY be\n"
34152 << "performed using the element's area associated to the halo(ed)\n"
34153 << "elements.\n"
34154 << "1) Make sure you have enabled the parallel mesh adaptation\n"
34155 << "option if you are working with a distributed mesh, OR\n"
34156 << "2) Make sure to call the update_..._using_elements_area() methods\n"
34157 << "if the mesh is marked as distributed\n\n";
34158 throw OomphLibError(
34159 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
34160 } // if (this->is_mesh_distributed())
34161#endif
34162
34163 // Boolean that indicates whether an actual update of the polygon
34164 // was performed or not
34165 bool unrefinement_was_performed = false;
34166 bool refinement_was_performed = false;
34167 bool max_length_applied = false;
34168
34169 // Loop over the number of polylines
34170 const unsigned n_polyline = polygon_pt->npolyline();
34171
34172 // Get face mesh representation of all polylines, possibly
34173 // with segments re-distributed to maintain an approximately
34174 // even sub-division of the polygon
34175 Vector<Mesh*> face_mesh_pt;
34176 get_face_mesh_representation(polygon_pt, face_mesh_pt);
34177
34178 // Create vertices for the polylines by using the vertices
34179 // of the FaceElements
34180 Vector<double> vertex_coord(3); // zeta,x,y
34181 Vector<double> bound_left(1);
34182 Vector<double> bound_right(1);
34183
34184 for (unsigned p = 0; p < n_polyline; p++)
34185 {
34186 // Set of coordinates that will be placed on the boundary
34187 // Set entries are ordered on first entry in vector which stores
34188 // the boundary coordinate so the vertices come out in order!
34189 std::set<Vector<double>> vertex_nodes;
34190
34191 // Get the boundary id
34192 const unsigned bound = polygon_pt->curve_section_pt(p)->boundary_id();
34193
34194 // Get the chunk number
34195 const unsigned chunk = polygon_pt->curve_section_pt(p)->boundary_chunk();
34196
34197 // Loop over the face elements (ordered) and add their vertices
34198 unsigned n_face_element = face_mesh_pt[p]->nelement();
34199 for (unsigned e = 0; e < n_face_element; ++e)
34200 {
34201 FiniteElement* el_pt = face_mesh_pt[p]->finite_element_pt(e);
34202
34203#ifdef OOMPH_HAS_MPI
34204 // Only work with non-halo elements if the mesh is distributed
34205 if (this->is_mesh_distributed() && el_pt->is_halo())
34206 {
34207 continue;
34208 }
34209#endif
34210
34211 unsigned n_node = el_pt->nnode();
34212
34213 // Add the left-hand node to the set:
34214
34215 // Boundary coordinate
34216 el_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
34217 vertex_coord[0] = bound_left[0];
34218
34219 // Actual coordinates
34220 for (unsigned i = 0; i < 2; i++)
34221 {
34222 vertex_coord[i + 1] = el_pt->node_pt(0)->x(i);
34223 }
34224 vertex_nodes.insert(vertex_coord);
34225
34226 // Add the right-hand nodes to the set:
34227
34228 // Boundary coordinate
34229 el_pt->node_pt(n_node - 1)
34230 ->get_coordinates_on_boundary(bound, bound_right);
34231 vertex_coord[0] = bound_right[0];
34232
34233 // Actual coordinates
34234 for (unsigned i = 0; i < 2; i++)
34235 {
34236 vertex_coord[i + 1] = el_pt->node_pt(n_node - 1)->x(i);
34237 }
34238 vertex_nodes.insert(vertex_coord);
34239 }
34240
34241 // Now turn into vector for ease of handling...
34242 unsigned n_poly_vertex = vertex_nodes.size();
34243 Vector<Vector<double>> tmp_vector_vertex_node(n_poly_vertex);
34244 unsigned count = 0;
34245 for (std::set<Vector<double>>::iterator it = vertex_nodes.begin();
34246 it != vertex_nodes.end();
34247 ++it)
34248 {
34249 tmp_vector_vertex_node[count].resize(3);
34250 tmp_vector_vertex_node[count][0] = (*it)[0];
34251 tmp_vector_vertex_node[count][1] = (*it)[1];
34252 tmp_vector_vertex_node[count][2] = (*it)[2];
34253 ++count;
34254 }
34255
34256 // Size of the vector
34257 unsigned n_vertex = tmp_vector_vertex_node.size();
34258
34259 // Tolerance below which the middle point can be deleted
34260 // (ratio of deflection to element length)
34261 double unrefinement_tolerance =
34262 polygon_pt->polyline_pt(p)->unrefinement_tolerance();
34263
34264 //------------------------------------------------------
34265 // Unrefinement
34266 //------------------------------------------------------
34267 if (unrefinement_tolerance > 0.0 && n_vertex >= 3)
34268 {
34269 unrefinement_was_performed = unrefine_boundary(bound,
34270 chunk,
34271 tmp_vector_vertex_node,
34272 unrefinement_tolerance,
34273 check_only);
34274
34275 // In this case the "unrefinement_was_performed" variable
34276 // tell us if the update had been performed when calling
34277 // with check_oly=false
34278 if (check_only && unrefinement_was_performed)
34279 {
34280 // Cleanup (but only the elements -- the nodes still exist in
34281 // the bulk mesh!
34282 for (unsigned p = 0; p < n_polyline; p++)
34283 {
34284 face_mesh_pt[p]->flush_node_storage();
34285 delete face_mesh_pt[p];
34286 }
34287 return true;
34288 }
34289
34290 } // end of unrefinement
34291
34292 // Do not perform refinement if there are no more than two vertices
34293 // New size of the vector
34294 n_vertex = tmp_vector_vertex_node.size();
34295
34296 //------------------------------------------------
34297 // Refinement
34298 //------------------------------------------------
34299 double refinement_tolerance =
34300 polygon_pt->polyline_pt(p)->refinement_tolerance();
34301 if (refinement_tolerance > 0.0 && n_vertex >= 2)
34302 {
34303 refinement_was_performed = refine_boundary(face_mesh_pt[p],
34304 tmp_vector_vertex_node,
34305 refinement_tolerance,
34306 check_only);
34307
34308 // In this case the "refinement_was_performed" variable
34309 // tell us if the update had been performed when calling
34310 // with check_only=false
34311 if (check_only && refinement_was_performed)
34312 {
34313 // Cleanup (but only the elements -- the nodes still exist in
34314 // the bulk mesh!
34315 for (unsigned p = 0; p < n_polyline; p++)
34316 {
34317 face_mesh_pt[p]->flush_node_storage();
34318 delete face_mesh_pt[p];
34319 }
34320 return true;
34321 }
34322
34323 } // end refinement
34324
34325 // Do not perform maximum length constraint if there are no more than
34326 // two vertices
34327 // New size of the vector
34328 n_vertex = tmp_vector_vertex_node.size();
34329
34330 //------------------------------------------------
34331 // Maximum length constrait
34332 //-----------------------------------------------
34333 double maximum_length = polygon_pt->polyline_pt(p)->maximum_length();
34334 if (maximum_length > 0.0 && n_vertex >= 2)
34335 {
34336 max_length_applied = apply_max_length_constraint(
34337 face_mesh_pt[p], tmp_vector_vertex_node, maximum_length);
34338
34339 // In this case the max length criteria was applied, check if
34340 // check_only=false
34341 if (check_only && max_length_applied)
34342 {
34343 // Cleanup (but only the elements -- the nodes still exist in
34344 // the bulk mesh!
34345 for (unsigned p = 0; p < n_polyline; p++)
34346 {
34347 face_mesh_pt[p]->flush_node_storage();
34348 delete face_mesh_pt[p];
34349 }
34350 return true;
34351 }
34352 }
34353
34354 // For further processing the three-dimensional vector
34355 // has to be reduced to a two-dimensional vector
34356 n_vertex = tmp_vector_vertex_node.size();
34357 Vector<Vector<double>> vector_vertex_node(n_vertex);
34358
34359 for (unsigned i = 0; i < n_vertex; i++)
34360 {
34361 vector_vertex_node[i].resize(2);
34362 vector_vertex_node[i][0] = tmp_vector_vertex_node[i][1];
34363 vector_vertex_node[i][1] = tmp_vector_vertex_node[i][2];
34364 }
34365
34366#ifdef OOMPH_HAS_MPI
34367 // Only perform this checking if the mesh is not distributed. When
34368 // the mesh is distributed the polylines continuity is addressed in
34369 // the sort_polylines_helper() method
34370 if (!this->is_mesh_distributed())
34371#endif
34372 {
34373 if ((p > 0) && !check_only)
34374 {
34375 // Final end point of previous line
34376 Vector<double> final_vertex_of_previous_segment;
34377 unsigned n_prev_vertex =
34378 polygon_pt->curve_section_pt(p - 1)->nvertex();
34379 final_vertex_of_previous_segment =
34380 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(n_prev_vertex -
34381 1);
34382
34383 unsigned prev_seg_boundary_id =
34384 polygon_pt->curve_section_pt(p - 1)->boundary_id();
34385
34386 // Find the error between the final vertex of the previous
34387 // line and the first vertex of the current line
34388 double error = 0.0;
34389 for (unsigned i = 0; i < 2; i++)
34390 {
34391 const double dist = final_vertex_of_previous_segment[i] -
34392 (*vector_vertex_node.begin())[i];
34393 error += dist * dist;
34394 }
34395 error = sqrt(error);
34396
34397 // If the error is bigger than the tolerance then
34398 // we probably need to reverse, but better check
34400 {
34401 // Find the error between the final vertex of the previous
34402 // line and the last vertex of the current line
34403 double rev_error = 0.0;
34404 for (unsigned i = 0; i < 2; i++)
34405 {
34406 const double dist = final_vertex_of_previous_segment[i] -
34407 (*--vector_vertex_node.end())[i];
34408 rev_error += dist * dist;
34409 }
34410 rev_error = sqrt(rev_error);
34411
34412 if (rev_error >
34414 {
34415 // It could be possible that the first segment be reversed and we
34416 // did not notice it because this check does not apply for the
34417 // first segment. We can verify if the first segment is reversed
34418 // by using the vertex number 1
34419 if (p == 1)
34420 {
34421 // Initial end point of previous line
34422 Vector<double> initial_vertex_of_previous_segment;
34423
34424 initial_vertex_of_previous_segment =
34425 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(0);
34426
34427 unsigned prev_seg_boundary_id =
34428 polygon_pt->curve_section_pt(p - 1)->boundary_id();
34429
34430 // Find the error between the initial vertex of the previous
34431 // line and the first vertex of the current line
34432 double error = 0.0;
34433 for (unsigned i = 0; i < 2; i++)
34434 {
34435 const double dist = initial_vertex_of_previous_segment[i] -
34436 (*vector_vertex_node.begin())[i];
34437 error += dist * dist;
34438 }
34439 error = sqrt(error); // Reversed only the previous one
34440
34441 // If the error is bigger than the tolerance then
34442 // we probably need to reverse, but better check
34443 if (error >
34445 {
34446 // Find the error between the final vertex of the previous
34447 // line and the last vertex of the current line
34448 double rev_error = 0.0;
34449 for (unsigned i = 0; i < 2; i++)
34450 {
34451 const double dist = initial_vertex_of_previous_segment[i] -
34452 (*--vector_vertex_node.end())[i];
34453 rev_error += dist * dist;
34454 }
34455 rev_error =
34456 sqrt(rev_error); // Reversed both the current one and
34457 // the previous one
34458
34459 if (rev_error >
34461 {
34462 std::ostringstream error_stream;
34463 error_stream
34464 << "The distance between the first node of the current\n"
34465 << "line segment (boundary " << bound
34466 << ") and either end of "
34467 << "the previous line segment\n"
34468 << "(boundary " << prev_seg_boundary_id
34469 << ") is bigger than "
34470 << "the desired tolerance "
34472 << ".\n"
34473 << "This suggests that the polylines defining the "
34474 "polygonal\n"
34475 << "representation are not properly ordered.\n"
34476 << "Fail on last vertex of polyline: ("
34477 << prev_seg_boundary_id
34478 << ") and\nfirst vertex of polyline (" << bound
34479 << ").\nThis should have failed when first trying to "
34480 << "construct the\npolygon.\n";
34481 throw OomphLibError(error_stream.str(),
34482 OOMPH_CURRENT_FUNCTION,
34483 OOMPH_EXCEPTION_LOCATION);
34484 }
34485 else
34486 {
34487 // Reverse both
34488 // Reverse the current vector to line up with the previous
34489 // one
34490 std::reverse(vector_vertex_node.begin(),
34491 vector_vertex_node.end());
34492
34493 polygon_pt->polyline_pt(p - 1)->reverse();
34494 }
34495 }
34496 else
34497 {
34498 // Reverse the previous one
34499 polygon_pt->polyline_pt(p - 1)->reverse();
34500 }
34501
34502 } // if p == 1
34503 else
34504 {
34505 std::ostringstream error_stream;
34506 error_stream
34507 << "The distance between the first node of the current\n"
34508 << "line segment (boundary " << bound
34509 << ") and either end of "
34510 << "the previous line segment\n"
34511 << "(boundary " << prev_seg_boundary_id
34512 << ") is bigger than the "
34513 << "desired tolerance "
34515 << ".\n"
34516 << "This suggests that the polylines defining the polygonal\n"
34517 << "representation are not properly ordered.\n"
34518 << "Fail on last vertex of polyline: ("
34519 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
34520 << bound << ").\n"
34521 << "This should have failed when first trying to construct "
34522 "the\n"
34523 << "polygon.\n";
34524 throw OomphLibError(error_stream.str(),
34525 OOMPH_CURRENT_FUNCTION,
34526 OOMPH_EXCEPTION_LOCATION);
34527 }
34528 }
34529 else
34530 {
34531 // Reverse the current vector to line up with the previous one
34532 std::reverse(vector_vertex_node.begin(),
34533 vector_vertex_node.end());
34534 }
34535
34536 } // first error
34537 } // p > 0
34538 } // is mesh not distributed?
34539
34540 if (!check_only)
34541 {
34542 // Now update the polyline according to the new vertices
34543 // The new one representation
34544 TriangleMeshPolyLine* tmp_polyline_pt =
34545 new TriangleMeshPolyLine(vector_vertex_node, bound);
34546
34547 // Create a temporal "curve section" version of the recently created
34548 // polyline
34549 TriangleMeshCurveSection* tmp_curve_section_pt = tmp_polyline_pt;
34550
34551 // Establish refinement and unrefinement tolerance
34552 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
34553 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
34554
34555 // Establish the maximum length constraint
34556 tmp_polyline_pt->set_maximum_length(maximum_length);
34557
34558 // We pass the connection information from the old polyline to
34559 // the new one
34560 this->copy_connection_information(polygon_pt->polyline_pt(p),
34561 tmp_curve_section_pt);
34562
34563 // Now update the polyline according to the new vertices but
34564 // first check if the object is allowed to delete the representation
34565 // or if it should be done by other object
34566 bool delete_it_on_destructor = false;
34567
34568 std::set<TriangleMeshCurveSection*>::iterator it =
34569 this->Free_curve_section_pt.find(polygon_pt->curve_section_pt(p));
34570
34571 if (it != this->Free_curve_section_pt.end())
34572 {
34573 this->Free_curve_section_pt.erase(it);
34574 delete polygon_pt->curve_section_pt(p);
34575 delete_it_on_destructor = true;
34576 }
34577
34578 // ------------------------------------------------------------
34579 // Copying the new representation
34580 polygon_pt->curve_section_pt(p) = tmp_polyline_pt;
34581
34582 // Update the Boundary - Polyline map
34583 this->Boundary_curve_section_pt[bound] =
34584 polygon_pt->curve_section_pt(p);
34585
34586 if (delete_it_on_destructor)
34587 {
34588 this->Free_curve_section_pt.insert(polygon_pt->curve_section_pt(p));
34589 }
34590
34591 } // if(!check_only)
34592
34593 } // for (p < n_polyline)
34594
34595 // Cleanup (but only the elements -- the nodes still exist in
34596 // the bulk mesh!
34597 for (unsigned p = 0; p < n_polyline; p++)
34598 {
34599 face_mesh_pt[p]->flush_node_storage();
34600 delete face_mesh_pt[p];
34601 }
34602
34603 if (check_only)
34604 {
34605 // if we end up all the way down here, no update of the internal
34606 // boundaries is necessary (in case we only check)
34607 return false;
34608 }
34609 else
34610 {
34611 // if we not only check, but actually perform the update and end up
34612 // all the way down here then we indicate whether an update was performed
34613 // or not
34614 return (unrefinement_was_performed || refinement_was_performed ||
34615 max_length_applied);
34616 }
34617 }
34618
34619 //=========================================================================
34620 /// Helper function that updates the input open curve by using
34621 /// end-points of elements from FaceMesh(es) that are constructed for the
34622 /// boundaries associated with the polylines. Optional boolean is used to
34623 /// run it as test only (if true is specified as input) in which case the
34624 /// polylines are not actually modified. Returned boolean indicates if
34625 /// polylines were (or would have been -- if called with check_only=false)
34626 /// changed.
34627 //=========================================================================
34628 template<class ELEMENT>
34630 TriangleMeshOpenCurve* open_polyline_pt, const bool& check_only)
34631 {
34632#ifdef PARANOID
34633 // If the mesh is marked as distributed this method can not be
34634 // called since there is no guarantee of creating (distributed)
34635 // meshes that match in the number and position of nodes at their
34636 // shared boundaries. The only exececption is when called with
34637 // check_only=true, since no boundary updating is performed
34638 if (this->is_mesh_distributed() && !check_only)
34639 {
34640 std::stringstream error_message;
34641 error_message
34642 << "The updating of open curves of a distributed mesh can ONLY be\n"
34643 << "performed using the element's area associated to the halo(ed)\n"
34644 << "elements.\n"
34645 << "1) Make sure you have enabled the parallel mesh adaptation\n"
34646 << "option if you are working with a distributed mesh, OR\n"
34647 << "2) Make sure to call the update_..._using_elements_area() methods\n"
34648 << "if the mesh is marked as distributed\n\n";
34649 throw OomphLibError(
34650 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
34651 } // if (this->is_mesh_distributed())
34652#endif
34653
34654 // Boolean that indicates whether an actual update of the polylines
34655 // were performed or not
34656 bool unrefinement_was_performed = false;
34657 bool refinement_was_performed = false;
34658 bool max_length_applied = false;
34659
34660 // Loop over the number of polylines
34661 const unsigned n_polyline = open_polyline_pt->ncurve_section();
34662
34663 // Get face mesh representation of all polylines, possibly
34664 // with segments re-distributed to maintain an approximately
34665 // even sub-division of the polygon
34666 Vector<Mesh*> face_mesh_pt;
34667 get_face_mesh_representation(open_polyline_pt, face_mesh_pt);
34668
34669 // Create vertices for the polylines by using the vertices
34670 // of the FaceElements
34671 Vector<double> vertex_coord(3); // zeta,x,y
34672 Vector<double> bound_left(1);
34673 Vector<double> bound_right(1);
34674
34675 for (unsigned p = 0; p < n_polyline; p++)
34676 {
34677 // Set of coordinates that will be placed on the boundary
34678 // Set entries are ordered on first entry in vector which stores
34679 // the boundary coordinate so the vertices come out in order!
34680 std::set<Vector<double>> vertex_nodes;
34681
34682 // Get the boundary id
34683 const unsigned bound =
34684 open_polyline_pt->curve_section_pt(p)->boundary_id();
34685
34686 // Get the chunk number
34687 const unsigned chunk =
34688 open_polyline_pt->curve_section_pt(p)->boundary_chunk();
34689
34690 // Loop over the face elements (ordered) and add their vertices
34691 unsigned n_face_element = face_mesh_pt[p]->nelement();
34692
34693 // n_count = 0;
34694 for (unsigned e = 0; e < n_face_element; ++e)
34695 {
34696 FiniteElement* el_pt = face_mesh_pt[p]->finite_element_pt(e);
34697 unsigned n_node = el_pt->nnode();
34698
34699 // Add the left-hand node to the set:
34700
34701 // Boundary coordinate
34702 el_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
34703 vertex_coord[0] = bound_left[0];
34704
34705 // Actual coordinates
34706 for (unsigned i = 0; i < 2; i++)
34707 {
34708 vertex_coord[i + 1] = el_pt->node_pt(0)->x(i);
34709 }
34710 vertex_nodes.insert(vertex_coord);
34711
34712 // Add the right-hand nodes to the set:
34713
34714 // Boundary coordinate
34715 el_pt->node_pt(n_node - 1)
34716 ->get_coordinates_on_boundary(bound, bound_right);
34717 vertex_coord[0] = bound_right[0];
34718
34719 // Actual coordinates
34720 for (unsigned i = 0; i < 2; i++)
34721 {
34722 vertex_coord[i + 1] = el_pt->node_pt(n_node - 1)->x(i);
34723 }
34724 vertex_nodes.insert(vertex_coord);
34725 }
34726
34727 // Now turn into vector for ease of handling...
34728 unsigned n_poly_vertex = vertex_nodes.size();
34729 Vector<Vector<double>> tmp_vector_vertex_node(n_poly_vertex);
34730 unsigned count = 0;
34731 for (std::set<Vector<double>>::iterator it = vertex_nodes.begin();
34732 it != vertex_nodes.end();
34733 ++it)
34734 {
34735 tmp_vector_vertex_node[count].resize(3);
34736 tmp_vector_vertex_node[count][0] = (*it)[0];
34737 tmp_vector_vertex_node[count][1] = (*it)[1];
34738 tmp_vector_vertex_node[count][2] = (*it)[2];
34739 ++count;
34740 }
34741
34742 // Size of the vector
34743 unsigned n_vertex = tmp_vector_vertex_node.size();
34744
34745 // Tolerance below which the middle point can be deleted
34746 // (ratio of deflection to element length)
34747 double unrefinement_tolerance =
34748 open_polyline_pt->polyline_pt(p)->unrefinement_tolerance();
34749
34750 //------------------------------------------------------
34751 // Unrefinement
34752 //------------------------------------------------------
34753 if (unrefinement_tolerance > 0.0 && n_vertex >= 3)
34754 {
34755 unrefinement_was_performed = unrefine_boundary(bound,
34756 chunk,
34757 tmp_vector_vertex_node,
34758 unrefinement_tolerance,
34759 check_only);
34760
34761 // In this case the unrefinement_was_performed variable actually
34762 // tell us if the update had been performed when calling
34763 // with check_only=false
34764 if (check_only && unrefinement_was_performed)
34765 {
34766 // Cleanup (but only the elements -- the nodes still exist in
34767 // the bulk mesh!
34768 for (unsigned p = 0; p < n_polyline; p++)
34769 {
34770 face_mesh_pt[p]->flush_node_storage();
34771 delete face_mesh_pt[p];
34772 }
34773 return true;
34774 }
34775
34776 } // end of unrefinement
34777
34778 // Do not perform refinement if there are no more than two vertices
34779 // (open curve version)
34780 // New size of the vector
34781 n_vertex = tmp_vector_vertex_node.size();
34782
34783 //------------------------------------------------
34784 /// Refinement
34785 //------------------------------------------------
34786 double refinement_tolerance =
34787 open_polyline_pt->polyline_pt(p)->refinement_tolerance();
34788 if (refinement_tolerance > 0.0 && n_vertex >= 2)
34789 {
34790 refinement_was_performed = refine_boundary(face_mesh_pt[p],
34791 tmp_vector_vertex_node,
34792 refinement_tolerance,
34793 check_only);
34794
34795 // In this case the unrefinement_was_performed variable actually
34796 // tell us if the update had been performed when calling
34797 // with check_only=false
34798 if (check_only && refinement_was_performed)
34799 {
34800 // Cleanup (but only the elements -- the nodes still exist in
34801 // the bulk mesh!
34802 for (unsigned p = 0; p < n_polyline; p++)
34803 {
34804 face_mesh_pt[p]->flush_node_storage();
34805 delete face_mesh_pt[p];
34806 }
34807 return true;
34808 }
34809
34810 } // end refinement
34811
34812 // Do not perform maximum length constraint if there are no more than
34813 // two vertices
34814 // New size of the vector
34815 n_vertex = tmp_vector_vertex_node.size();
34816
34817 //------------------------------------------------
34818 // Maximum length constraint
34819 //-----------------------------------------------
34820 double maximum_length =
34821 open_polyline_pt->polyline_pt(p)->maximum_length();
34822 if (maximum_length > 0.0 && n_vertex >= 2)
34823 {
34824 bool max_length_applied = false;
34825 max_length_applied = apply_max_length_constraint(
34826 face_mesh_pt[p], tmp_vector_vertex_node, maximum_length);
34827
34828 // In this case the max length criteria was applied, check if
34829 // check_only=false
34830 if (check_only && max_length_applied)
34831 {
34832 // Cleanup (but only the elements -- the nodes still exist in
34833 // the bulk mesh!
34834 for (unsigned p = 0; p < n_polyline; p++)
34835 {
34836 face_mesh_pt[p]->flush_node_storage();
34837 delete face_mesh_pt[p];
34838 }
34839 return true;
34840 }
34841 }
34842
34843 // For further processing the three-dimensional vector
34844 // has to be reduced to a two-dimensional vector
34845 n_vertex = tmp_vector_vertex_node.size();
34846 Vector<Vector<double>> vector_vertex_node(n_vertex);
34847
34848 for (unsigned i = 0; i < n_vertex; i++)
34849 {
34850 vector_vertex_node[i].resize(2);
34851 vector_vertex_node[i][0] = tmp_vector_vertex_node[i][1];
34852 vector_vertex_node[i][1] = tmp_vector_vertex_node[i][2];
34853 }
34854
34855#ifdef OOMPH_HAS_MPI
34856 // Only perform this checking if the mesh is not distributed. When
34857 // the mesh is distributed the polylines continuity is addressed
34858 // in the sort_polylines_helper() method
34859 if (!this->is_mesh_distributed())
34860#endif
34861 {
34862 // Check whether the segments are continguous (first vertex of this
34863 // segment is equal to last vertex of previous segment).
34864 // If not, we should reverse the order of the current segment.
34865 // This check only applies for segments other than the first.
34866 // We only bother with this check, if we actually perform an update
34867 // of the polyline, i.e. if it's not only a check
34868 if ((p > 0) && !check_only)
34869 {
34870 // Final end point of previous line
34871 Vector<double> final_vertex_of_previous_segment;
34872 open_polyline_pt->polyline_pt(p - 1)->final_vertex_coordinate(
34873 final_vertex_of_previous_segment);
34874
34875 unsigned prev_seg_boundary_id =
34876 open_polyline_pt->curve_section_pt(p - 1)->boundary_id();
34877
34878 // Find the error between the final vertex of the previous
34879 // line and the first vertex of the current line
34880 double error = 0.0;
34881 for (unsigned i = 0; i < 2; i++)
34882 {
34883 const double dist = final_vertex_of_previous_segment[i] -
34884 (*vector_vertex_node.begin())[i];
34885 error += dist * dist;
34886 }
34887 error = sqrt(error);
34888
34889 // If the error is bigger than the tolerance then
34890 // we probably need to reverse, but better check
34892 {
34893 // Find the error between the final vertex of the previous
34894 // line and the first vertex of the current line
34895 error = 0.0;
34896 for (unsigned i = 0; i < 2; i++)
34897 {
34898 const double dist = final_vertex_of_previous_segment[i] -
34899 (*--vector_vertex_node.end())[i];
34900 error += dist * dist;
34901 }
34902 error = sqrt(error);
34903
34905 {
34906 // It could be possible that the first segment be reversed
34907 // and we did not notice it because this check does not
34908 // apply for the first segment. We can verify if the first
34909 // segment is reversed by using the vertex number 1
34910 if (p == 1)
34911 {
34912 // If no found it is possible that the previous polyline
34913 // be reversed Check for that case Initial point of
34914 // previous line
34915 Vector<double> initial_vertex_of_previous_segment;
34916 open_polyline_pt->polyline_pt(p - 1)->initial_vertex_coordinate(
34917 initial_vertex_of_previous_segment);
34918
34919 // Find the error between the initial vertex of the previous
34920 // line and the first vertex of the current line
34921 error = 0.0;
34922 for (unsigned i = 0; i < 2; i++)
34923 {
34924 const double dist = initial_vertex_of_previous_segment[i] -
34925 (*vector_vertex_node.begin())[i];
34926 error += dist * dist;
34927 }
34928 error = sqrt(error);
34929
34930 // If the error is bigger than the tolerance then
34931 // we probably need to reverse, but better check
34932 if (error >
34934 {
34935 // Find the error between the final vertex of the previous
34936 // line and the first vertex of the current line
34937 error = 0.0;
34938 for (unsigned i = 0; i < 2; i++)
34939 {
34940 const double dist = initial_vertex_of_previous_segment[i] -
34941 (*--vector_vertex_node.end())[i];
34942 error += dist * dist;
34943 }
34944 error = sqrt(error);
34945
34946 if (error >
34948 {
34949 std::ostringstream error_stream;
34950 error_stream
34951 << "The distance between the first node of the current\n"
34952 << "line segment (boundary " << bound
34953 << ") and either end of the previous line segment\n"
34954 << "(boundary " << prev_seg_boundary_id
34955 << ") is bigger than "
34956 << "the desired tolerance "
34958 << ".\n"
34959 << "This suggests that the polylines defining the open "
34960 << "curve\n"
34961 << "representation are not properly ordered.\n"
34962 << "Fail on last vertex of polyline: ("
34963 << prev_seg_boundary_id
34964 << ") and\nfirst vertex of polyline (" << bound << ").\n"
34965 << "This should have failed when first trying to "
34966 "construct\n"
34967 << "the open curve.\n";
34968 throw OomphLibError(error_stream.str(),
34969 OOMPH_CURRENT_FUNCTION,
34970 OOMPH_EXCEPTION_LOCATION);
34971 }
34972 else // We have to reverse both
34973 {
34974 // First reverse the previous polyline
34975 open_polyline_pt->polyline_pt(p - 1)->reverse();
34976 // Then reverse the current polyline
34977 std::reverse(vector_vertex_node.begin(),
34978 vector_vertex_node.end());
34979 }
34980 }
34981 else
34982 {
34983 // Reverse the previous polyline only
34984 open_polyline_pt->polyline_pt(p - 1)->reverse();
34985 }
34986 } // if (p == 1)
34987 else
34988 {
34989 std::ostringstream error_stream;
34990 error_stream
34991 << "The distance between the first node of the current\n"
34992 << "line segment (boundary " << bound
34993 << ") and either end of "
34994 << "the previous line segment\n"
34995 << "(boundary " << prev_seg_boundary_id
34996 << ") is bigger than the "
34997 << "desired tolerance "
34999 << ".\n"
35000 << "This suggests that the polylines defining the polygonal\n"
35001 << "representation are not properly ordered.\n"
35002 << "Fail on last vertex of polyline: ("
35003 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
35004 << bound << ").\n"
35005 << "This should have failed when first trying to construct "
35006 "the\n"
35007 << "polygon.\n";
35008 throw OomphLibError(error_stream.str(),
35009 OOMPH_CURRENT_FUNCTION,
35010 OOMPH_EXCEPTION_LOCATION);
35011 }
35012 }
35013 else
35014 {
35015 // Reverse the current vector to line up with the previous one
35016 std::reverse(vector_vertex_node.begin(),
35017 vector_vertex_node.end());
35018 }
35019 }
35020
35021 } // if p > 0
35022
35023 } // is mesh not distributed?
35024
35025 if (!check_only)
35026 {
35027 // Now update the polyline according to the new vertices The new
35028 // one representation
35029 TriangleMeshPolyLine* tmp_polyline =
35030 new TriangleMeshPolyLine(vector_vertex_node, bound);
35031
35032 // Create a temporal "curve section" version of the recently
35033 // created polyline
35034 TriangleMeshCurveSection* tmp_curve_section = tmp_polyline;
35035
35036 // Copy the unrefinement and refinement information
35037 tmp_polyline->set_unrefinement_tolerance(unrefinement_tolerance);
35038 tmp_polyline->set_refinement_tolerance(refinement_tolerance);
35039
35040 // Establish the maximum length constraint
35041 tmp_polyline->set_maximum_length(maximum_length);
35042
35043 // Pass the connection information from the old polyline to the
35044 // new one
35045 this->copy_connection_information(open_polyline_pt->polyline_pt(p),
35046 tmp_curve_section);
35047
35048 std::set<TriangleMeshCurveSection*>::iterator it =
35049 this->Free_curve_section_pt.find(
35050 open_polyline_pt->curve_section_pt(p));
35051
35052 bool delete_it_on_destructor = false;
35053
35054 if (it != this->Free_curve_section_pt.end())
35055 {
35056 // Free previous representation only if you created
35057 this->Free_curve_section_pt.erase(it);
35058 delete open_polyline_pt->curve_section_pt(p);
35059 delete_it_on_destructor = true;
35060 }
35061
35062 // *****************************************************************
35063 // Copying the new representation
35064 open_polyline_pt->curve_section_pt(p) = tmp_polyline;
35065
35066 // Update the Boundary <--> PolyLine map
35067 this->Boundary_curve_section_pt[bound] =
35068 open_polyline_pt->curve_section_pt(p);
35069
35070 if (delete_it_on_destructor)
35071 {
35072 this->Free_curve_section_pt.insert(
35073 open_polyline_pt->curve_section_pt(p));
35074 }
35075
35076 } // if(!check_only)
35077
35078 } // n_polylines
35079
35080 // Cleanup (but only the elements -- the nodes still exist in
35081 // the bulk mesh!
35082 for (unsigned p = 0; p < n_polyline; p++)
35083 {
35084 face_mesh_pt[p]->flush_node_storage();
35085 delete face_mesh_pt[p];
35086 }
35087
35088 if (check_only)
35089 {
35090 // if we end up all the way down here, no update of the internal
35091 // boundaries is necessary (in case we only check)
35092 return false;
35093 }
35094 else
35095 {
35096 // if we not only check, but actually perform the update and end
35097 // up all the way down here then we indicate whether an update was
35098 // performed or not
35099 return (unrefinement_was_performed || refinement_was_performed ||
35100 max_length_applied);
35101 }
35102 }
35103
35104 //=========================================================================
35105 /// Helper function that performs the unrefinement process
35106 /// on the specified boundary by using the provided vertices
35107 /// representation. Optional boolean is used to run it as test only (if
35108 /// true is specified as input) in which case vertex coordinates aren't
35109 /// actually modified. Returned boolean indicates if polyline was (or
35110 /// would have been -- if called with check_only=false) changed.
35111 //=========================================================================
35112 template<class ELEMENT>
35114 const unsigned& b,
35115 const unsigned& c,
35116 Vector<Vector<double>>& vector_bnd_vertices,
35117 double& unrefinement_tolerance,
35118 const bool& check_only)
35119 {
35120 // Store the vertices not allowed for deletion
35121 std::set<Vector<double>> no_delete_vertex;
35122
35123 // Does the boundary receives connections?
35124 const bool boundary_receive_connections =
35125 this->boundary_connections(b, c, no_delete_vertex);
35126
35127 // Boolean that indicates whether an actual update of the vertex
35128 // coordinates was performed or not
35129 bool unrefinement_was_performed = false;
35130
35131 unsigned n_vertex = vector_bnd_vertices.size();
35132
35133 // Initialise counter that indicates at which vertex we're currently
35134 // considering for deletion
35135 unsigned counter = 1;
35136
35137 // Loop over the nodes; start with the second one and increment by two
35138 // this way a "pack" of three nodes will be considered for calculation:
35139 // the middle-node (which is to be deleted or not) and the adjacent
35140 // nodes
35141 for (unsigned i = 1; i <= n_vertex - 2; i += 2)
35142 {
35143 // Maths from http://www.cgafaq.info/wiki/Circle_Through_Three_Points
35144 double a_x = vector_bnd_vertices[i - 1][1];
35145 double a_y = vector_bnd_vertices[i - 1][2];
35146 double b_x = vector_bnd_vertices[i][1];
35147 double b_y = vector_bnd_vertices[i][2];
35148 double c_x = vector_bnd_vertices[i + 1][1];
35149 double c_y = vector_bnd_vertices[i + 1][2];
35150
35151 double a = b_x - a_x;
35152 double b = b_y - a_y;
35153 double c = c_x - a_x;
35154 double d = c_y - a_y;
35155
35156 double e = a * (a_x + b_x) + b * (a_y + b_y);
35157 double f = c * (a_x + c_x) + d * (a_y + c_y);
35158
35159 double g = 2.0 * (a * (c_y - b_y) - b * (c_x - b_x));
35160
35161 bool do_it = false;
35162 if (std::fabs(g) < 1.0e-14)
35163 {
35164 do_it = true;
35165 if (check_only)
35166 {
35167 return true;
35168 }
35169 }
35170 else
35171 {
35172 double p_x = (d * e - b * f) / g;
35173 double p_y = (a * f - c * e) / g;
35174
35175 double r = sqrt(pow((a_x - p_x), 2) + pow((a_y - p_y), 2));
35176
35177 double rhalfca_x = 0.5 * (a_x - c_x);
35178 double rhalfca_y = 0.5 * (a_y - c_y);
35179
35180 double halfca_squared = pow(rhalfca_x, 2) + pow(rhalfca_y, 2);
35181
35182 double sticky_out_bit = r - sqrt(std::fabs((r * r) - halfca_squared));
35183
35184 // If sticky out bit divided by distance between end nodes
35185 // is less than tolerance the boundary is so flat that we
35186 // can safely kill the node
35187 if ((sticky_out_bit / (2.0 * sqrt(halfca_squared))) <
35188 unrefinement_tolerance)
35189 {
35190 do_it = true;
35191 if (check_only)
35192 {
35193 return true;
35194 }
35195 }
35196 }
35197
35198 // If the vertex was proposed for deletion check that it is
35199 // allowed for being deleted
35200 if (do_it && boundary_receive_connections)
35201 {
35202 // Is the vertex one of the non deletable vertices
35203 for (std::set<Vector<double>>::iterator it = no_delete_vertex.begin();
35204 it != no_delete_vertex.end();
35205 it++)
35206 {
35207 // Compute the distance between the proposed node to delete
35208 // and the ones that should not be deleted
35209 const double x = (*it)[0];
35210 const double y = (*it)[1];
35211 double error = (b_x - x) * (b_x - x) + (b_y - y) * (b_y - y);
35212 error = sqrt(error);
35213
35215 {
35216 // Do not delete the vertex
35217 do_it = false;
35218 break;
35219 }
35220 }
35221
35222 } // if (do_it && boundary_receive_connections)
35223
35224 // Remove node?
35225 if (do_it)
35226 {
35227 vector_bnd_vertices[i].resize(0);
35228 }
35229
35230 // Increase the counter, that indicates the number of the
35231 // next middle node
35232 counter += 2;
35233 }
35234
35235 // coming out of here the value of counter is the index of the
35236 // last node on the polyline counter=n_vertex-1 (in case of an
35237 // even number of nodes) or counter has the value of the number
35238 // of nodes on the polyline counter=n_vertex (in case of an odd
35239 // number of nodes
35240
35241 // Special treatment for the end of the polyline:
35242 // If the number of nodes is even, then the previous loop stopped
35243 // at the last but second node, i.e. the current value of counter
35244 // is the index of the last node. If that's the case, the last but
35245 // one node needs to be treated separately
35246 if ((counter) == (n_vertex - 1))
35247 {
35248 // Set the last but one node as middle node
35249 unsigned i = vector_bnd_vertices.size() - 2;
35250
35251 // Index of the current! last but second node (considering any
35252 // previous deletion)
35253 unsigned n = 0;
35254
35255 if (vector_bnd_vertices[counter - 2].size() != 0)
35256 {
35257 // if the initial last but second node does still exist then
35258 // this one is obviously also the current last but second one
35259 n = counter - 2;
35260 }
35261 else
35262 {
35263 // if the initial last but second node was deleted then the
35264 // initial last but third node is the current last but second
35265 // node
35266 n = counter - 3;
35267 }
35268
35269 // CODE DUPLICATION -- CAN'T BE BOTHERED TO WRITE A SEPARATE
35270 // FUNCTION FOR THIS; PROBABLY WORTH DOING IF/WHEN THERE'S
35271 // A MISTAKE IN ANY OF THIS AND IT NEEDS TO BE FIXED...
35272
35273 // Maths from http://www.cgafaq.info/wiki/Circle_Through_Three_Points
35274 double a_x = vector_bnd_vertices[n][1];
35275 double a_y = vector_bnd_vertices[n][2];
35276 double b_x = vector_bnd_vertices[i][1];
35277 double b_y = vector_bnd_vertices[i][2];
35278 double c_x = vector_bnd_vertices[i + 1][1];
35279 double c_y = vector_bnd_vertices[i + 1][2];
35280
35281 double a = b_x - a_x;
35282 double b = b_y - a_y;
35283 double c = c_x - a_x;
35284 double d = c_y - a_y;
35285
35286 double e = a * (a_x + b_x) + b * (a_y + b_y);
35287 double f = c * (a_x + c_x) + d * (a_y + c_y);
35288
35289 double g = 2.0 * (a * (c_y - b_y) - b * (c_x - b_x));
35290
35291 bool do_it = false;
35292 if (std::fabs(g) < 1.0e-14)
35293 {
35294 do_it = true;
35295 if (check_only)
35296 {
35297 return true;
35298 }
35299 }
35300 else
35301 {
35302 double p_x = (d * e - b * f) / g;
35303 double p_y = (a * f - c * e) / g;
35304
35305 double r = sqrt(pow((a_x - p_x), 2) + pow((a_y - p_y), 2));
35306
35307 double rhalfca_x = 0.5 * (a_x - c_x);
35308 double rhalfca_y = 0.5 * (a_y - c_y);
35309
35310 double halfca_squared = pow(rhalfca_x, 2) + pow(rhalfca_y, 2);
35311
35312 double sticky_out_bit = r - sqrt(std::fabs((r * r) - halfca_squared));
35313
35314 // If sticky out bit divided by distance between end nodes
35315 // is less than tolerance the boundary is so flat that we
35316 // can safely kill the node
35317 if ((sticky_out_bit / (2.0 * sqrt(halfca_squared))) <
35318 unrefinement_tolerance)
35319 {
35320 do_it = true;
35321 if (check_only)
35322 {
35323 return true;
35324 }
35325 }
35326 }
35327
35328 // If the vertex was proposed for deletion check that it is
35329 // allowed for being deleted
35330 if (do_it && boundary_receive_connections)
35331 {
35332 // Is the vertex one of the non deletable vertices
35333 for (std::set<Vector<double>>::iterator it = no_delete_vertex.begin();
35334 it != no_delete_vertex.end();
35335 it++)
35336 {
35337 // Compute the distance between the proposed node to delete
35338 // and the ones that should not be deleted
35339 const double x = (*it)[0];
35340 const double y = (*it)[1];
35341 double error = (b_x - x) * (b_x - x) + (b_y - y) * (b_y - y);
35342 error = sqrt(error);
35343
35345 {
35346 // Do not delete the vertex
35347 do_it = false;
35348 break;
35349 }
35350 }
35351
35352 } // if (do_it && boundary_receive_connections)
35353
35354 // Remove node?
35355 if (do_it)
35356 {
35357 vector_bnd_vertices[i].resize(0);
35358 }
35359 }
35360
35361 // Create another vector, which will only contain entries of
35362 // nodes that still exist
35363 Vector<Vector<double>> compact_vector;
35364 compact_vector.reserve(n_vertex);
35365 for (unsigned i = 0; i < n_vertex; i++)
35366 {
35367 // If the entry was not deleted include it in the new vector
35368 if (vector_bnd_vertices[i].size() != 0)
35369 {
35370 compact_vector.push_back(vector_bnd_vertices[i]);
35371 }
35372 }
35373
35374 /// Get the size of the vector that now includes all remaining nodes
35375 n_vertex = compact_vector.size();
35376
35377 // If the size of the vector containing the remaining nodes is
35378 // different from the size of the vector before the unrefinement
35379 // routine (with the original nodes)
35380 // then the polyline was obviously updated
35381 if (n_vertex != vector_bnd_vertices.size())
35382 {
35383 unrefinement_was_performed = true;
35384 }
35385
35386 /// Copy back
35387 vector_bnd_vertices.resize(n_vertex);
35388 for (unsigned i = 0; i < n_vertex; i++)
35389 {
35390 vector_bnd_vertices[i].resize(3);
35391 vector_bnd_vertices[i][0] = compact_vector[i][0];
35392 vector_bnd_vertices[i][1] = compact_vector[i][1];
35393 vector_bnd_vertices[i][2] = compact_vector[i][2];
35394 }
35395
35396 return unrefinement_was_performed;
35397 }
35398
35399 //=========================================================================
35400 /// Helper function that performs the refinement process
35401 /// on the specified boundary by using the provided vertices
35402 /// representation. Optional boolean is used to run it as test only (if
35403 /// true is specified as input) in which case vertex coordinates aren't
35404 /// actually modified. Returned boolean indicates if polyline was (or
35405 /// would have been -- if called with check_only=false) changed.
35406 //=========================================================================
35407 template<class ELEMENT>
35409 Mesh* face_mesh_pt,
35410 Vector<Vector<double>>& vector_bnd_vertices,
35411 double& refinement_tolerance,
35412 const bool& check_only)
35413 {
35414 // Boolean that indicates whether an actual update of the vertex
35415 // coordinates was performed or not
35416 bool refinement_was_performed = false;
35417
35418 // Create a geometric object from the mesh to represent
35419 // the curvilinear boundary
35420 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(face_mesh_pt);
35421
35422 // Get the total number of current vertices
35423 unsigned n_vertex = vector_bnd_vertices.size();
35424
35425 // Create a new (temporary) vector for the nodes, so
35426 // that new nodes can be stored
35427 Vector<Vector<double>> extended_vector;
35428
35429 // Reserve memory space for twice the number of already
35430 // existing nodes (worst case)
35431 extended_vector.reserve(2 * n_vertex);
35432
35433 // Loop over the nodes until the last but one node
35434 for (unsigned inod = 0; inod < n_vertex - 1; inod++)
35435 {
35436 // Get local coordinate of "left" node
35437 double zeta_left = vector_bnd_vertices[inod][0];
35438
35439 // Get position vector of "left" node
35440 Vector<double> R_left(2);
35441 for (unsigned i = 0; i < 2; i++)
35442 {
35443 R_left[i] = vector_bnd_vertices[inod][i + 1];
35444 }
35445
35446 // Get local coordinate of "right" node
35447 double zeta_right = vector_bnd_vertices[inod + 1][0];
35448
35449 // Get position vector of "right" node
35450 Vector<double> R_right(2);
35451 for (unsigned i = 0; i < 2; i++)
35452 {
35453 R_right[i] = vector_bnd_vertices[inod + 1][i + 1];
35454 }
35455
35456 // Get the boundary coordinate of the midpoint
35457 Vector<double> zeta_mid(1);
35458 zeta_mid[0] = 0.5 * (zeta_left + zeta_right);
35459
35460 // Get the position vector of the midpoint on the
35461 // curvilinear boundary
35462 Vector<double> R_mid(2);
35463 mesh_geom_obj_pt->position(zeta_mid, R_mid);
35464
35465 // Get the position vector of the midpoint on the straight
35466 // line connecting "left" and "right" node
35467 Vector<double> R_mid_polygon(2);
35468 for (unsigned i = 0; i < 2; i++)
35469 {
35470 R_mid_polygon[i] = 0.5 * (R_right[i] + R_left[i]);
35471 }
35472
35473 // Calculate the distance between the midpoint on the curvilinear
35474 // boundary and the midpoint on the straight line
35475 double distance =
35476 sqrt((R_mid[0] - R_mid_polygon[0]) * (R_mid[0] - R_mid_polygon[0]) +
35477 (R_mid[1] - R_mid_polygon[1]) * (R_mid[1] - R_mid_polygon[1]));
35478
35479 // Calculating the length of the straight line
35480 double length = sqrt((R_right[0] - R_left[0]) * (R_right[0] - R_left[0]) +
35481 (R_right[1] - R_left[1]) * (R_right[1] - R_left[1]));
35482
35483 // If the ratio of distance between the midpoints to the length
35484 // of the straight line is larger than the tolerance
35485 // specified for the criterion when points can be deleted,
35486 // create a new node and add it to the (temporary) vector
35487 if ((distance / length) > refinement_tolerance)
35488 {
35489 if (check_only)
35490 {
35491 // Delete the allocated memory for the geometric object
35492 // that represents the curvilinear boundary
35493 delete mesh_geom_obj_pt;
35494 return true;
35495 }
35496
35497 Vector<double> new_node(3);
35498 new_node[0] = zeta_mid[0];
35499 new_node[1] = R_mid[0];
35500 new_node[2] = R_mid[1];
35501
35502 // Include the "left" node in the new "temporary" vector
35503 extended_vector.push_back(vector_bnd_vertices[inod]);
35504
35505 // Include the new node as well
35506 extended_vector.push_back(new_node);
35507 }
35508 else
35509 {
35510 // Include the "left" node in the new "temporary" vector
35511 // and move on to the next node
35512 extended_vector.push_back(vector_bnd_vertices[inod]);
35513 }
35514 } // end of loop over nodes
35515
35516 // Add the last node to the vector
35517 extended_vector.push_back(vector_bnd_vertices[n_vertex - 1]);
35518
35519 /// Get the size of the vector that now includes all added nodes
35520 n_vertex = extended_vector.size();
35521
35522 // If the size of the vector including the added nodes is
35523 // different from the size of the vector before the refinement
35524 // routine then the polyline was obviously updated
35525 if (n_vertex != vector_bnd_vertices.size())
35526 {
35527 refinement_was_performed = true;
35528 }
35529
35530 // Copy across
35531 vector_bnd_vertices.resize(n_vertex);
35532 for (unsigned i = 0; i < n_vertex; i++)
35533 {
35534 vector_bnd_vertices[i].resize(3);
35535 vector_bnd_vertices[i][0] = extended_vector[i][0];
35536 vector_bnd_vertices[i][1] = extended_vector[i][1];
35537 vector_bnd_vertices[i][2] = extended_vector[i][2];
35538 }
35539
35540 // Delete the allocated memory for the geometric object
35541 // that represents the curvilinear boundary
35542 delete mesh_geom_obj_pt;
35543
35544 return refinement_was_performed;
35545 }
35546
35547 //=========================================================================
35548 // Helper function that applies the maximum length constraint
35549 // when it was specified. This will increase the number of points in
35550 // the current curve section in case that any segment on it does not
35551 // fulfils the requirement
35552 //=========================================================================
35553 template<class ELEMENT>
35555 Mesh* face_mesh_pt,
35556 Vector<Vector<double>>& vector_bnd_vertices,
35557 double& max_length_constraint)
35558 {
35559 // Boolean that indicates whether an actual update of the vertex
35560 // coordinates was performed or not
35561 bool max_length_applied = false;
35562
35563 // Create a geometric object from the mesh to represent
35564 // the curvilinear boundary
35565 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(face_mesh_pt);
35566
35567 // Get the total number of current vertices
35568 unsigned n_vertex = vector_bnd_vertices.size();
35569
35570 // Create a new (temporary) vector for the nodes, so
35571 // that new nodes can be stored
35572 Vector<Vector<double>> extended_vector;
35573
35574 // Loop over the nodes until the last but one node
35575 for (unsigned inod = 0; inod < n_vertex - 1; inod++)
35576 {
35577 // Get local coordinate of "left" node
35578 double zeta_left = vector_bnd_vertices[inod][0];
35579
35580 // Get position vector of "left" node
35581 Vector<double> R_left(2);
35582 for (unsigned i = 0; i < 2; i++)
35583 {
35584 R_left[i] = vector_bnd_vertices[inod][i + 1];
35585 }
35586
35587 // Get local coordinate of "right" node
35588 double zeta_right = vector_bnd_vertices[inod + 1][0];
35589
35590 // Get position vector of "right" node
35591 Vector<double> R_right(2);
35592 for (unsigned i = 0; i < 2; i++)
35593 {
35594 R_right[i] = vector_bnd_vertices[inod + 1][i + 1];
35595 }
35596
35597 // Include the "left" node in the new "temporary" vector
35598 extended_vector.push_back(vector_bnd_vertices[inod]);
35599
35600 // Check whether the current distance between the left and right node
35601 // is longer than the specified constraint or not
35602 double length = std::fabs(zeta_right - zeta_left);
35603
35604 // Do we need to introduce new nodes?
35605 if (length > max_length_constraint)
35606 {
35607 double n_pts = length / max_length_constraint;
35608 // We only want the integer part
35609 unsigned n_points = static_cast<unsigned>(n_pts);
35610 double zeta_increment =
35611 (zeta_right - zeta_left) / ((double)n_points + 1);
35612
35613 Vector<double> zeta(1);
35614 // Create the n_points+1 points inside the segment
35615 for (unsigned s = 1; s < n_points + 1; s++)
35616 {
35617 // Get the coordinates
35618 zeta[0] = zeta_left + zeta_increment * double(s);
35619 Vector<double> vertex(2);
35620 mesh_geom_obj_pt->position(zeta, vertex);
35621
35622 // Create the new node
35623 Vector<double> new_node(3);
35624 new_node[0] = zeta[0];
35625 new_node[1] = vertex[0];
35626 new_node[2] = vertex[1];
35627
35628 // Include the new node
35629 extended_vector.push_back(new_node);
35630 }
35631 }
35632 }
35633
35634 // Add the last node to the vector
35635 extended_vector.push_back(vector_bnd_vertices[n_vertex - 1]);
35636
35637 /// Get the size of the vector that now includes all added nodes
35638 n_vertex = extended_vector.size();
35639
35640 // If the size of the vector including the added nodes is
35641 // different from the size of the vector before applying the maximum length
35642 // constraint then the polyline was obviously updated
35643 if (n_vertex != vector_bnd_vertices.size())
35644 {
35645 max_length_applied = true;
35646 }
35647
35648 // Copy across
35649 vector_bnd_vertices.resize(n_vertex);
35650 for (unsigned i = 0; i < n_vertex; i++)
35651 {
35652 vector_bnd_vertices[i].resize(3);
35653 vector_bnd_vertices[i][0] = extended_vector[i][0];
35654 vector_bnd_vertices[i][1] = extended_vector[i][1];
35655 vector_bnd_vertices[i][2] = extended_vector[i][2];
35656 }
35657
35658 // Delete the allocated memory for the geometric object
35659 // that represents the curvilinear boundary
35660 delete mesh_geom_obj_pt;
35661
35662 return max_length_applied;
35663 }
35664
35665 //=========================================================================
35666 /// Helper function
35667 /// Creates an unsorted face mesh representation from the specified
35668 /// boundary id. It means that the elements are not sorted along the
35669 /// boundary
35670 //=========================================================================
35671 template<class ELEMENT>
35673 create_unsorted_face_mesh_representation(const unsigned& boundary_id,
35674 Mesh* face_mesh_pt)
35675 {
35676 // Create a face mesh adjacent to specified boundary.
35677 // The face mesh consists of FaceElements that may also be
35678 // interpreted as GeomObjects
35679
35680 // Build the face mesh
35681 this->template build_face_mesh<ELEMENT, FaceElementAsGeomObject>(
35682 boundary_id, face_mesh_pt);
35683
35684 // Find the total number of added elements
35685 unsigned n_element = face_mesh_pt->nelement();
35686 // Loop over the elements
35687 for (unsigned e = 0; e < n_element; e++)
35688 {
35689 // Cast the element pointer to the correct thing!
35691 dynamic_cast<FaceElementAsGeomObject<ELEMENT>*>(
35692 face_mesh_pt->element_pt(e));
35693
35694 // Set bulk boundary number
35695 el_pt->set_boundary_number_in_bulk_mesh(boundary_id);
35696 }
35697 }
35698
35699 //=========================================================================
35700 /// Helper function
35701 /// Creates a sorted face mesh representation of the specified PolyLine
35702 /// It means that the elements are sorted along the boundary
35703 //=========================================================================
35704 template<class ELEMENT>
35706 const unsigned& boundary_id,
35707 Mesh* face_mesh_pt,
35708 std::map<FiniteElement*, bool>& is_inverted,
35709 bool& inverted_face_mesh)
35710 {
35711 Mesh* tmp_unsorted_face_mesh_pt = new Mesh();
35712
35713 // First step we get the unsorted version of the face mesh
35714 create_unsorted_face_mesh_representation(boundary_id,
35715 tmp_unsorted_face_mesh_pt);
35716
35717 // Once with the unsorted version of the face mesh
35718 // only left to sort it out!!!
35719
35720 // Put all face elements in order
35721 //-------------------------------
35722
35723 // Put first element into ordered list
35724 // Temporal list for sorting the elements
35725 std::list<FiniteElement*> sorted_el_pt;
35726 FiniteElement* el_pt = tmp_unsorted_face_mesh_pt->finite_element_pt(0);
35727 sorted_el_pt.push_back(el_pt);
35728
35729 // Number of nodes
35730 unsigned nnod = el_pt->nnode();
35731
35732 // Count elements that have been done
35733 unsigned count_done = 0;
35734
35735 // How many face elements are there?
35736 unsigned n_face_element = tmp_unsorted_face_mesh_pt->nelement();
35737
35738 // Keep track of who's done
35739 std::map<FiniteElement*, bool> done_el;
35740
35741 is_inverted.clear();
35742
35743 // Fit in the other elements in at most nel^2 loops
35744 for (unsigned ee = 1; ee < n_face_element; ee++)
35745 {
35746 // Loop over all elements to check if they fit to the right
35747 // or the left of the current one
35748 for (unsigned e = 1; e < n_face_element; e++)
35749 {
35750 // Candidate element
35751 el_pt = tmp_unsorted_face_mesh_pt->finite_element_pt(e);
35752
35753 // Is it done yet?
35754 if (!done_el[el_pt])
35755 {
35756 // Left and rightmost elements
35757 FiniteElement* first_el_pt = (*sorted_el_pt.begin());
35758 std::list<FiniteElement*>::iterator it = sorted_el_pt.end();
35759 it--;
35760 FiniteElement* last_el_pt = *it;
35761
35762 // Left and rightmost nodes
35763 Node* left_node_pt = first_el_pt->node_pt(0);
35764 if (is_inverted[first_el_pt])
35765 {
35766 left_node_pt = first_el_pt->node_pt(nnod - 1);
35767 }
35768 Node* right_node_pt = last_el_pt->node_pt(nnod - 1);
35769 if (is_inverted[last_el_pt])
35770 {
35771 right_node_pt = last_el_pt->node_pt(0);
35772 }
35773
35774 // New element fits at the left of first element and is not inverted
35775 if (left_node_pt == el_pt->node_pt(nnod - 1))
35776 {
35777 sorted_el_pt.push_front(el_pt);
35778 done_el[el_pt] = true;
35779 count_done++;
35780 is_inverted[el_pt] = false;
35781 }
35782 // New element fits at the left of first element and is inverted
35783
35784 else if (left_node_pt == el_pt->node_pt(0))
35785 {
35786 sorted_el_pt.push_front(el_pt);
35787 done_el[el_pt] = true;
35788 count_done++;
35789 is_inverted[el_pt] = true;
35790 }
35791 // New element fits on the right of last element and is not inverted
35792
35793 else if (right_node_pt == el_pt->node_pt(0))
35794 {
35795 sorted_el_pt.push_back(el_pt);
35796 done_el[el_pt] = true;
35797 count_done++;
35798 is_inverted[el_pt] = false;
35799 }
35800 // New element fits on the right of last element and is inverted
35801
35802 else if (right_node_pt == el_pt->node_pt(nnod - 1))
35803 {
35804 sorted_el_pt.push_back(el_pt);
35805 done_el[el_pt] = true;
35806 count_done++;
35807 is_inverted[el_pt] = true;
35808 }
35809
35810 if (done_el[el_pt])
35811 {
35812 break;
35813 }
35814 }
35815 }
35816 }
35817
35818 // Are we done?
35819 if (count_done != (n_face_element - 1))
35820 {
35821 std::ostringstream error_message;
35822 error_message << "When ordering FaceElements on "
35823 << "boundary " << boundary_id << " only managed to order \n"
35824 << count_done << " of " << n_face_element
35825 << " face elements.\n"
35826 << std::endl;
35827 throw OomphLibError(
35828 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
35829 }
35830
35831 // Now make a mesh that contains the FaceElements in order
35832 // Remember that we currently have a list, not a mesh of sorted elements
35833
35834 // Fill it
35835 for (std::list<FiniteElement*>::iterator it = sorted_el_pt.begin();
35836 it != sorted_el_pt.end();
35837 it++)
35838 {
35839 // Get element
35840 FiniteElement* el_pt = *it;
35841
35842 // add this face element to the order original mesh
35843 face_mesh_pt->add_element_pt(el_pt);
35844 }
35845
35846 // Verify if face mesh representation is not inverted according to the
35847 // polyline specified by the user, it means that the initial and the
35848 // final vertex does really correspond to the first and last vertex
35849 // respectively, if not, state that the face mesh representation is
35850 // inverted
35851
35852 // Get the associated polyline representation to the boundary
35853 TriangleMeshPolyLine* bnd_polyline =
35854 this->Boundary_curve_section_pt[boundary_id];
35855
35856 // Get the really first vertex
35857 Vector<double> first_vertex = bnd_polyline->vertex_coordinate(0);
35858
35859 // Now get the first node based on the face mesh representation
35860 // First get access to the first element
35861 FiniteElement* first_el_pt = face_mesh_pt->finite_element_pt(0);
35862
35863 // Now get access to the first node
35864 unsigned n_node = first_el_pt->nnode();
35865 // Get the very first node (taking into account if it is
35866 // inverted or not!!)
35867 Node* first_node_pt = first_el_pt->node_pt(0);
35868 if (is_inverted[first_el_pt])
35869 {
35870 first_node_pt = first_el_pt->node_pt(n_node - 1);
35871 }
35872
35873 double error = (first_node_pt->x(0) - first_vertex[0]) *
35874 (first_node_pt->x(0) - first_vertex[0]) +
35875 (first_node_pt->x(1) - first_vertex[1]) *
35876 (first_node_pt->x(1) - first_vertex[1]);
35877
35878 error = sqrt(error);
35879
35881 {
35882 inverted_face_mesh = false;
35883 }
35884 else
35885 {
35886 inverted_face_mesh = true;
35887 }
35888 }
35889
35890 //=========================================================================
35891 /// Helper function to construct face mesh representation of all polylines,
35892 /// possibly with segments re-distributed between polylines
35893 /// to maintain an approximately even sub-division of the polygon
35894 //=========================================================================
35895 template<class ELEMENT>
35897 TriangleMeshPolygon* polygon_pt, Vector<Mesh*>& face_mesh_pt)
35898 {
35899 // Number of polylines
35900 unsigned n_polyline = polygon_pt->npolyline();
35901 face_mesh_pt.resize(n_polyline);
35902
35903 // Are we eligible for re-distributing polyline segments between
35904 // polylines? We're not if any of the boundaries are associated
35905 // with a GeomObject because we're then tied to the start and
35906 // end coordinates along it.
35907 bool eligible_for_segment_redistribution = true;
35908
35909 // Loop over constituent polylines
35910 for (unsigned p = 0; p < n_polyline; p++)
35911 {
35912 // Get the boundary id of the polyline
35913 unsigned bound = polygon_pt->polyline_pt(p)->boundary_id();
35914
35915 // If the boundary has a geometric object representation then
35916 // we can't redistribute
35917 GeomObject* const geom_object_pt = this->boundary_geom_object_pt(bound);
35918 if (geom_object_pt != 0)
35919 {
35920 eligible_for_segment_redistribution = false;
35921 }
35922
35923 face_mesh_pt[p] = new Mesh();
35924 create_unsorted_face_mesh_representation(bound, face_mesh_pt[p]);
35925 }
35926
35928 {
35929 return;
35930 }
35931
35932 // If there is more than one region we have to think... Die for now.
35933 if (this->nregion() > 1)
35934 {
35935 std::ostringstream warn_message;
35936 warn_message
35937 << "Can't currently re-distribute segments between polylines if there\n"
35938 << "are multiple regions; returning..." << std::endl;
35939 OomphLibWarning(warn_message.str(),
35940 "RefineableTriangleMesh::get_face_mesh_representation()",
35941 OOMPH_EXCEPTION_LOCATION);
35942 return;
35943 }
35944
35945 // Redistribution overruled
35946 if (!eligible_for_segment_redistribution)
35947 {
35948 std::ostringstream warn_message;
35949 warn_message
35950 << "Over-ruling re-distribution of segments between polylines\n"
35951 << "because at least one boundary is associated with a GeomObject."
35952 << "Returning..." << std::endl;
35953 OomphLibWarning(warn_message.str(),
35954 "RefineableTriangleMesh::get_face_mesh_representation()",
35955 OOMPH_EXCEPTION_LOCATION);
35956 return;
35957 }
35958
35959 // Create a vector for ordered face mesh
35960 Vector<Mesh*> ordered_face_mesh_pt(n_polyline);
35961
35962 // Storage for the total arclength of polygon
35963 double s_total = 0.0;
35964
35965 // Storage for first and last nodes on polylines so we can figure
35966 // out if they are inverted relative to each other
35967 Vector<Node*> first_polyline_node_pt(n_polyline);
35968 Vector<Node*> last_polyline_node_pt(n_polyline);
35969 std::vector<bool> is_reversed(n_polyline, false);
35970
35971 // Loop over constituent polylines
35972 for (unsigned p = 0; p < n_polyline; p++)
35973 {
35974 // Put all face elements in order
35975 //-------------------------------
35976
35977 // Put first element into ordered list
35978 std::list<FiniteElement*> ordered_el_pt;
35979 FiniteElement* el_pt = face_mesh_pt[p]->finite_element_pt(0);
35980 ordered_el_pt.push_back(el_pt);
35981
35982 // Number of nodes
35983 unsigned nnod = el_pt->nnode();
35984
35985 // Default for first and last node on polyline
35986 first_polyline_node_pt[p] = el_pt->node_pt(0);
35987 last_polyline_node_pt[p] = el_pt->node_pt(nnod - 1);
35988
35989 // Count elements that have been done
35990 unsigned count_done = 0;
35991
35992 // How many face elements are there?
35993 unsigned n_face_element = face_mesh_pt[p]->nelement();
35994
35995 // Get the boundary id of the polyline
35996 unsigned bound = polygon_pt->polyline_pt(p)->boundary_id();
35997
35998 // Keep track of who's done
35999 std::map<FiniteElement*, bool> done_el;
36000
36001 // Keep track of which element is inverted
36002 std::map<FiniteElement*, bool> is_inverted;
36003
36004 // Fit in the other elements in at most nel^2 loops
36005 for (unsigned ee = 1; ee < n_face_element; ee++)
36006 {
36007 // Loop over all elements to check if they fit to the right
36008 // or the left of the current one
36009 for (unsigned e = 1; e < n_face_element; e++)
36010 {
36011 // Candidate element
36012 el_pt = face_mesh_pt[p]->finite_element_pt(e);
36013
36014 // Is it done yet?
36015 if (!done_el[el_pt])
36016 {
36017 // Left and rightmost elements
36018 FiniteElement* first_el_pt = (*ordered_el_pt.begin());
36019 std::list<FiniteElement*>::iterator it = ordered_el_pt.end();
36020 it--;
36021 FiniteElement* last_el_pt = *it;
36022
36023 // Left and rightmost nodes
36024 Node* left_node_pt = first_el_pt->node_pt(0);
36025 if (is_inverted[first_el_pt])
36026 {
36027 left_node_pt = first_el_pt->node_pt(nnod - 1);
36028 }
36029 Node* right_node_pt = last_el_pt->node_pt(nnod - 1);
36030 if (is_inverted[last_el_pt])
36031 {
36032 right_node_pt = last_el_pt->node_pt(0);
36033 }
36034
36035 // New element fits at the left of first element and is not inverted
36036 if (left_node_pt == el_pt->node_pt(nnod - 1))
36037 {
36038 ordered_el_pt.push_front(el_pt);
36039 done_el[el_pt] = true;
36040 count_done++;
36041 is_inverted[el_pt] = false;
36042 first_polyline_node_pt[p] = el_pt->node_pt(0);
36043 }
36044 // New element fits at the left of first element and is inverted
36045
36046 else if (left_node_pt == el_pt->node_pt(0))
36047 {
36048 ordered_el_pt.push_front(el_pt);
36049 done_el[el_pt] = true;
36050 count_done++;
36051 is_inverted[el_pt] = true;
36052 first_polyline_node_pt[p] = el_pt->node_pt(nnod - 1);
36053 }
36054 // New element fits on the right of last element and is not inverted
36055
36056 else if (right_node_pt == el_pt->node_pt(0))
36057 {
36058 ordered_el_pt.push_back(el_pt);
36059 done_el[el_pt] = true;
36060 count_done++;
36061 is_inverted[el_pt] = false;
36062 last_polyline_node_pt[p] = el_pt->node_pt(nnod - 1);
36063 }
36064 // New element fits on the right of last element and is inverted
36065
36066 else if (right_node_pt == el_pt->node_pt(nnod - 1))
36067 {
36068 ordered_el_pt.push_back(el_pt);
36069 done_el[el_pt] = true;
36070 count_done++;
36071 is_inverted[el_pt] = true;
36072 last_polyline_node_pt[p] = el_pt->node_pt(0);
36073 }
36074
36075 if (done_el[el_pt])
36076 {
36077 break;
36078 }
36079 }
36080 }
36081 }
36082
36083 // Are we done?
36084 if (count_done != (n_face_element - 1))
36085 {
36086 std::ostringstream error_message;
36087 error_message << "When ordering FaceElements on "
36088 << "boundary " << bound << " only managed to order \n"
36089 << count_done << " of " << n_face_element
36090 << " face elements.\n"
36091 << std::endl;
36092 throw OomphLibError(error_message.str(),
36093 OOMPH_CURRENT_FUNCTION,
36094 OOMPH_EXCEPTION_LOCATION);
36095 }
36096
36097 // Now make a mesh that contains the FaceElements in order
36098 ordered_face_mesh_pt[p] = new Mesh;
36099
36100 // Fill it
36101 for (std::list<FiniteElement*>::iterator it = ordered_el_pt.begin();
36102 it != ordered_el_pt.end();
36103 it++)
36104 {
36105 // Get element
36106 FiniteElement* el_pt = *it;
36107
36108 // add this face element to the order original mesh
36109 ordered_face_mesh_pt[p]->add_element_pt(el_pt);
36110 }
36111
36112 // Get the arclength along the polygon
36113 for (unsigned e = 0; e < n_face_element; ++e)
36114 {
36115 FiniteElement* el_pt = ordered_face_mesh_pt[p]->finite_element_pt(e);
36116 unsigned n_node = el_pt->nnode();
36117 double element_length_squared = 0.0;
36118 for (unsigned i = 0; i < 2; i++)
36119 {
36120 element_length_squared +=
36121 pow(el_pt->node_pt(n_node - 1)->x(i) - el_pt->node_pt(0)->x(i), 2);
36122 }
36123
36124 // Determine element length
36125 double element_length = sqrt(element_length_squared);
36126
36127 // Add this length to the total arclength
36128 s_total += element_length;
36129 }
36130
36131 // Empty the original meshes
36132 face_mesh_pt[p]->flush_element_and_node_storage();
36133 }
36134
36135 // Is first one reversed?
36136 if ((last_polyline_node_pt[0] == first_polyline_node_pt[1]) ||
36137 (last_polyline_node_pt[0] == last_polyline_node_pt[1]))
36138 {
36139 is_reversed[0] = false;
36140 }
36141 else if ((first_polyline_node_pt[0] == first_polyline_node_pt[1]) ||
36142 (first_polyline_node_pt[0] == last_polyline_node_pt[1]))
36143 {
36144 is_reversed[0] = true;
36145 }
36146
36147 // Reorder the face meshes so that they are contiguous
36148 Vector<Mesh*> tmp_face_mesh_pt(n_polyline);
36149 std::vector<bool> mesh_done(n_polyline, false);
36150 Vector<unsigned> old_polyline_number(n_polyline);
36151
36152 // Initial entry
36153 tmp_face_mesh_pt[0] = ordered_face_mesh_pt[0];
36154 unsigned current = 0;
36155 old_polyline_number[0] = 0;
36156 unsigned count_found = 0;
36157
36158 // Fill in the next entries
36159 for (unsigned p = 1; p < n_polyline; p++)
36160 {
36161 Node* end_node_pt = last_polyline_node_pt[current];
36162 if (is_reversed[current])
36163 {
36164 end_node_pt = first_polyline_node_pt[current];
36165 }
36166
36167 // Loop over all remaining face meshes to see which one fits
36168 for (unsigned pp = 1; pp < n_polyline; pp++)
36169 {
36170 if (!mesh_done[pp])
36171 {
36172 // Current one is not reversed, candidate is not reversed
36173 if ((!is_reversed[current]) &&
36174 (end_node_pt == first_polyline_node_pt[pp]))
36175 {
36176 tmp_face_mesh_pt[p] = ordered_face_mesh_pt[pp];
36177 mesh_done[pp] = true;
36178 is_reversed[pp] = false;
36179 old_polyline_number[p] = pp;
36180 current = pp;
36181 count_found++;
36182 break;
36183 }
36184 // Current one is not reversed, candidate is reversed
36185
36186 else if ((!is_reversed[current]) &&
36187 (end_node_pt == last_polyline_node_pt[pp]))
36188 {
36189 tmp_face_mesh_pt[p] = ordered_face_mesh_pt[pp];
36190 mesh_done[pp] = true;
36191 is_reversed[pp] = true;
36192 old_polyline_number[p] = pp;
36193 current = pp;
36194 count_found++;
36195 break;
36196 }
36197 // Current one is reversed, candidate is not reversed
36198
36199 else if ((is_reversed[current]) &&
36200 (end_node_pt == first_polyline_node_pt[pp]))
36201 {
36202 tmp_face_mesh_pt[p] = ordered_face_mesh_pt[pp];
36203 mesh_done[pp] = true;
36204 is_reversed[pp] = false;
36205 old_polyline_number[p] = pp;
36206 current = pp;
36207 count_found++;
36208 break;
36209 }
36210 // Current one is reversed, candidate is reversed
36211
36212 else if ((is_reversed[current]) &&
36213 (end_node_pt == last_polyline_node_pt[pp]))
36214 {
36215 tmp_face_mesh_pt[p] = ordered_face_mesh_pt[pp];
36216 mesh_done[pp] = true;
36217 is_reversed[pp] = true;
36218 old_polyline_number[p] = pp;
36219 current = pp;
36220 count_found++;
36221 break;
36222 }
36223 }
36224 }
36225 }
36226
36227#ifdef PARANOID
36228 if (count_found != n_polyline - 1)
36229 {
36230 std::ostringstream error_message;
36231 error_message << "Only found " << count_found << " out of "
36232 << n_polyline - 1 << " polylines to be fitted in.\n";
36233 throw OomphLibError(
36234 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36235 }
36236#endif
36237
36238 // Now overwrite the re-ordered data
36239 for (unsigned i = 0; i < n_polyline; i++)
36240 {
36241 ordered_face_mesh_pt[i] = tmp_face_mesh_pt[i];
36242 }
36243
36244 // Now do an approximate equidistribution of polylines
36245 //----------------------------------------------------
36246 double s = 0.0;
36247 unsigned new_face_id = 0;
36248
36249 // Matrix map to indicate if node must not be removed from specified
36250 // boundary (!=0) or not (=0). Initialises itself to zero
36251 std::map<Node*, std::map<unsigned, unsigned>>
36252 node_must_not_be_removed_from_boundary_flag;
36253
36254 // Loop over the old face mesh
36255 for (unsigned p = 0; p < n_polyline; p++)
36256 {
36257 // Loop over the face elements
36258 unsigned n_face_element = ordered_face_mesh_pt[p]->nelement();
36259 for (unsigned e = 0; e < n_face_element; e++)
36260 {
36261 unsigned el_number = e;
36262 if (is_reversed[p])
36263 {
36264 el_number = n_face_element - e - 1;
36265 }
36266
36267 FiniteElement* el_pt =
36268 ordered_face_mesh_pt[p]->finite_element_pt(el_number);
36269 unsigned n_node = el_pt->nnode();
36270
36271 // Determine element length
36272 double element_length_squared = 0.0;
36273 for (unsigned i = 0; i < 2; i++)
36274 {
36275 element_length_squared +=
36276 pow(el_pt->node_pt(n_node - 1)->x(i) - el_pt->node_pt(0)->x(i), 2);
36277 }
36278 double element_length = sqrt(element_length_squared);
36279
36280 // Add this length to the total arclength
36281 s += element_length;
36282
36283 // Check if the current 'arclength' is less than the
36284 // whole 'arclength' divided by the number of polylines
36285 if (s < s_total / double(n_polyline) + 1e-6)
36286 {
36287 // If so add this face element to the new face mesh
36288 face_mesh_pt[new_face_id]->add_element_pt(el_pt);
36289
36290 unsigned bound_old =
36291 polygon_pt->polyline_pt(old_polyline_number[p])->boundary_id();
36292
36293 unsigned bound_new =
36294 polygon_pt->polyline_pt(new_face_id)->boundary_id();
36295
36296 // Loop over the nodes in the element
36297 for (unsigned i = 0; i < n_node; i++)
36298 {
36299 // Get the pointer to the node
36300 Node* nod_pt = el_pt->node_pt(i);
36301
36302 // If the two boundary id's are different, the face element's nodes
36303 // have to be added to the new boundary
36304 if (bound_new != bound_old)
36305 {
36306 // Add it to the new boundary
36307 add_boundary_node(bound_new, nod_pt);
36308
36309 // We are happy for this node to be removed from the
36310 // old boundary?
36311 node_must_not_be_removed_from_boundary_flag[nod_pt][bound_old] +=
36312 0;
36313 }
36314
36315 // If the face element hasn't moved, its nodes MUST remain
36316 // on that boundary (incl. any nodes that ar shared by
36317 // FaceElements that have moved (see above)
36318
36319 else
36320 {
36321 node_must_not_be_removed_from_boundary_flag[nod_pt][bound_old] +=
36322 1;
36323 }
36324 }
36325 }
36326
36327 // If not, reset the current 'arclength' to zero,
36328 // increase the new face id by one and go one element
36329 // back by decreasing e by one to make sure the current
36330 // element gets added to the next face mesh
36331
36332 else
36333 {
36334 if (new_face_id != n_polyline - 1)
36335 {
36336 s = 0.0;
36337 new_face_id++;
36338 --e;
36339 }
36340 else
36341 {
36342 s = 0.0;
36343 --e;
36344 }
36345 }
36346 }
36347 } // end of loop over all polylines -- they are now re-distributed
36348
36349
36350 // Loop over all nodes on the boundaries of the polygon to remove
36351 // nodes from boundaries they are no longer on
36352 unsigned move_count = 0;
36353 for (std::map<Node*, std::map<unsigned, unsigned>>::iterator it =
36354 node_must_not_be_removed_from_boundary_flag.begin();
36355 it != node_must_not_be_removed_from_boundary_flag.end();
36356 it++)
36357 {
36358 // Get the node
36359 Node* nod_pt = (*it).first;
36360
36361 // Now we loop over the boundaries that this node is on
36362 for (std::map<unsigned, unsigned>::iterator it_2 = (*it).second.begin();
36363 it_2 != (*it).second.end();
36364 it_2++)
36365 {
36366 // Get the boundary id
36367 unsigned bound = (*it_2).first;
36368
36369 // Remove it from that boundary?
36370 if ((*it_2).second == 0)
36371 {
36372 remove_boundary_node(bound, nod_pt);
36373 move_count++;
36374 }
36375 }
36376 }
36377
36378 // Loop over the new face mesh to assign new boundary IDs
36379 for (unsigned p = 0; p < n_polyline; p++)
36380 {
36381 // Get the boundary id of the polyline
36382 unsigned bound = polygon_pt->polyline_pt(p)->boundary_id();
36383
36384 // Loop over the face elements
36385 unsigned n_face_element = face_mesh_pt[p]->nelement();
36386 for (unsigned e = 0; e < n_face_element; e++)
36387 {
36388 // Cast the element pointer to the correct thing!
36390 dynamic_cast<FaceElementAsGeomObject<ELEMENT>*>(
36391 face_mesh_pt[p]->element_pt(e));
36392
36393 // Set bulk boundary number
36395 }
36396 }
36397
36398 // Update look-up for elements next to boundary
36399 setup_boundary_element_info();
36400
36401 // Now re-create the boundary coordinates
36402 for (unsigned p = 0; p < n_polyline; p++)
36403 {
36404 // Get the boundary id of the polyline
36405 unsigned bound = polygon_pt->polyline_pt(p)->boundary_id();
36406
36407 // Do it
36408 this->template setup_boundary_coordinates<ELEMENT>(bound);
36409 }
36410
36411 // Clean up
36412 for (unsigned p = 0; p < n_polyline; p++)
36413 {
36414 // Flush the nodes from the face mesh to make sure we
36415 // don't delete them (the face mesh that we're returning from here
36416 // still needs them!)
36417 ordered_face_mesh_pt[p]->flush_element_and_node_storage();
36418 delete ordered_face_mesh_pt[p];
36419 }
36420 }
36421
36422 //=========================================================================
36423 /// Helper function to construct face mesh representation of all polylines
36424 //=========================================================================
36425 template<class ELEMENT>
36427 TriangleMeshOpenCurve* open_polyline_pt, Vector<Mesh*>& face_mesh_pt)
36428 {
36429 // Number of polylines
36430 unsigned n_polyline = open_polyline_pt->ncurve_section();
36431 face_mesh_pt.resize(n_polyline);
36432
36433 // Loop over constituent polylines
36434 for (unsigned p = 0; p < n_polyline; p++)
36435 {
36436 // Get the boundary id of the polyline
36437 unsigned bound = open_polyline_pt->curve_section_pt(p)->boundary_id();
36438
36439 face_mesh_pt[p] = new Mesh();
36440 create_unsorted_face_mesh_representation(bound, face_mesh_pt[p]);
36441 }
36442 }
36443
36444 //======================================================================
36445 /// Update the PSLG that define the inner boundaries of the mesh.
36446 /// Optional boolean is used to run it as test only (if
36447 /// true is specified as input) in which case PSLG isn't actually
36448 /// modified. Returned boolean indicates if PSLG was (or would have
36449 /// been -- if called with check_only=false) changed.
36450 //======================================================================
36451 template<class ELEMENT>
36453 ELEMENT>::surface_remesh_for_inner_hole_boundaries(Vector<Vector<double>>&
36454 internal_point_coord,
36455 const bool& check_only)
36456 {
36457 // Boolean to indicate whether an actual update of the internal
36458 // holes was performed
36459 bool update_was_performed = false;
36460 // Loop over the number of internal boundaries
36461 unsigned n_hole = internal_point_coord.size();
36462 for (unsigned ihole = 0; ihole < n_hole; ihole++)
36463 {
36464 // Cache the pointer to the polygon representation
36465 TriangleMeshPolygon* const poly_pt = this->Internal_polygon_pt[ihole];
36466
36467
36468 // Can the polygon update its own configuration, in which case this
36469 // is easy
36471 {
36473
36474 // Initialize Vector hole_coordinates
36475 internal_point_coord[ihole].resize(2);
36476
36477 // Get the vector of hole coordinates
36478 internal_point_coord[ihole] = poly_pt->internal_point();
36479 }
36480 // Otherwise we have to work much harder
36481
36482 else
36483 {
36484 // if we only want to check whether an update of the inner
36485 // hole is necessary
36486 if (check_only)
36487 {
36488 // is it necessary?
36489 bool update_necessary =
36490 this->update_polygon_using_face_mesh(poly_pt, check_only);
36491
36492 // Yes?
36493 if (update_necessary)
36494 {
36495 // then we have to adaptand return 'true'
36496 return true;
36497 }
36498 }
36499 // if we not only want to check, then we actually perform
36500 // the update
36501 else
36502 {
36503 update_was_performed = this->update_polygon_using_face_mesh(poly_pt);
36504 }
36505
36506 // Now we need to sort out the hole coordinates
36507 if (!poly_pt->internal_point().empty())
36508 {
36509 // If fixed don't update and simply
36510 // Read out the existing value
36511 if (poly_pt->is_internal_point_fixed())
36512 {
36513 // Get the vector of hole coordinates
36514 internal_point_coord[ihole] = poly_pt->internal_point();
36515 }
36516 // This is where the work starts and this could be made much
36517 // better than the current hack
36518 else
36519 {
36520 // If the user has set their own function then use that
36521 if (this->Internal_hole_point_update_fct_pt != 0)
36522 {
36523 this->Internal_hole_point_update_fct_pt(ihole, poly_pt);
36524 }
36525 // Otherwise use our clunky default
36526 else
36527 {
36528 // Now sort out the hole coordinates
36529 Vector<double> vertex_coord;
36530 unsigned n_polyline = poly_pt->npolyline();
36531
36532 // Initialize Vector hole_coordinates
36533 vertex_coord.resize(2);
36534 internal_point_coord[ihole].resize(2);
36535
36536 // Hole centre will be found by averaging the position of
36537 // all vertex nodes
36538 internal_point_coord[ihole][0] = 0.0;
36539 internal_point_coord[ihole][1] = 0.0;
36540
36541 for (unsigned p = 0; p < n_polyline; p++)
36542 {
36543 Vector<double> poly_ave(2, 0.0);
36544 // How many vertices are there in the segment
36545 unsigned n_vertex = poly_pt->polyline_pt(p)->nvertex();
36546 for (unsigned v = 0; v < n_vertex; v++)
36547 {
36548 vertex_coord = poly_pt->polyline_pt(p)->vertex_coordinate(v);
36549 for (unsigned i = 0; i < 2; i++)
36550 {
36551 poly_ave[i] += vertex_coord[i];
36552 }
36553 }
36554
36555 // Add the average polyline coordinate to the hole centre
36556 for (unsigned i = 0; i < 2; i++)
36557 {
36558 internal_point_coord[ihole][i] += poly_ave[i] / n_vertex;
36559 }
36560 }
36561
36562 // Now average out the hole centre
36563 for (unsigned i = 0; i < 2; i++)
36564 {
36565 internal_point_coord[ihole][i] /= n_polyline;
36566 }
36567
36568 // We have now found the hole centre stored in
36569 // internal_point_coordinate[ihole][i]
36570
36571 // Find polylines that intersect at y average value
36572 // Alice's version but this does not work if the end point of a
36573 // segment is the intersection point (i.e. at the y average value)
36574 /*Vector<double> vertex_coord2;
36575 unsigned n_intersect=0;
36576 double x_average=0.0;
36577
36578 for(unsigned p=0;p<n_polyline;p++)
36579 {
36580 //How many vertices are there in the segment
36581 unsigned n_vertex = poly_pt->polyline_pt(p)->nvertex();
36582 for(unsigned v=0;v<n_vertex-1;v++)
36583 {
36584 vertex_coord = poly_pt->polyline_pt(p)->vertex_coordinate(v);
36585 vertex_coord2 = poly_pt->polyline_pt(p)->vertex_coordinate(v+1);
36586 std::cout << vertex_coord[0] << " " << vertex_coord[1]
36587 << " " <<
36588 vertex_coord2[0] << " " <<
36589
36590 vertex_coord2[1] << "\n";
36591 //Does the line between vertices intersect the vertical position
36592 if((vertex_coord[1] -internal_point_coord[ihole][1])*
36593 (vertex_coord2[1] - internal_point_coord[ihole][1]) < 0.0)
36594 {
36595 ++n_intersect; x_average += 0.5*(vertex_coord[0] +
36596 vertex_coord2[0]);
36597 }
36598 }
36599 }
36600
36601 //Now just report the value if we have had intersections
36602 if(n_intersect != 0)
36603 {
36604 //Report
36605 std::cout << "I have computed a hole " << x_average << " " <<
36606 n_intersect << " "
36607 << x_average/((double)n_intersect) << std::endl;
36608 internal_point_coord[ihole][0] =
36609 x_average/((double)n_intersect);
36610 }
36611 */
36612
36613 // Set the new hole centre
36614 poly_pt->internal_point() = internal_point_coord[ihole];
36615 // std::cout << "I've had my centre updated to "
36616 // << internal_point_coord[ihole][0]
36617 // << " " << internal_point_coord[ihole][1] << "\n";
36618 }
36619 }
36620 }
36621 }
36622 } // End of the action (n_hole for)
36623
36624 if (check_only)
36625 {
36626 // If we make it up to here and we only check then no update is required
36627 return false;
36628 }
36629 else
36630 {
36631 // otherwise indicate whether an actual update was performed
36632 return update_was_performed;
36633 }
36634
36635 } // End of the loop of internal boundaries
36636
36637 //======================================================================
36638 /// Create the polylines and fill associate data structures, used when
36639 /// creating from a mesh from polyfiles
36640 //======================================================================
36641 template<class ELEMENT>
36643 const std::string& node_file_name, const std::string& poly_file_name)
36644 {
36645 // Get the nodes coordinates (the index of the nodes to build the
36646 // polylines is the one used in the node_file_name file)
36647 // Process node file
36648 // -----------------
36649 std::ifstream node_file(node_file_name.c_str(), std::ios_base::in);
36650
36651 // Check that the file actually opened correctly
36652 if (!node_file.is_open())
36653 {
36654 std::string error_msg("Failed to open node file: ");
36655 error_msg += "\"" + node_file_name + "\".";
36656 throw OomphLibError(
36657 error_msg, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36658 }
36659
36660 // Read number of nodes
36661 unsigned nnodes;
36662 node_file >> nnodes;
36663
36664 // Spatial dimension of nodes
36665 unsigned dimension;
36666 node_file >> dimension;
36667
36668#ifdef PARANOID
36669 if (dimension != 2)
36670 {
36671 throw OomphLibError("The dimension must be 2\n",
36672 OOMPH_CURRENT_FUNCTION,
36673 OOMPH_EXCEPTION_LOCATION);
36674 }
36675#endif
36676
36677 // Storage the nodes vertices
36678 Vector<double> x_node(nnodes);
36679 Vector<double> y_node(nnodes);
36680
36681 // Number of attributes
36682 unsigned npoint_attributes;
36683 node_file >> npoint_attributes;
36684 ;
36685
36686 // Flag for boundary markers
36687 unsigned boundary_markers_flag = 0;
36688 node_file >> boundary_markers_flag;
36689
36690 // Dummy for node number
36691 unsigned dummy_node_number;
36692 // Dummy for node attribute
36693 unsigned dummy_node_attribute;
36694 // Dummy for node boundary
36695 unsigned dummy_node_boundary;
36696
36697 // Load in nodal posititions, point attributes
36698 // and boundary markers
36699 for (unsigned i = 0; i < nnodes; i++)
36700 {
36701 node_file >> dummy_node_number;
36702 node_file >> x_node[i];
36703 node_file >> y_node[i];
36704 for (unsigned j = 0; j < npoint_attributes; ++j)
36705 {
36706 node_file >> dummy_node_attribute;
36707 }
36708 if (boundary_markers_flag)
36709 {
36710 node_file >> dummy_node_boundary;
36711 }
36712 }
36713 node_file.close();
36714
36715 // Get the segments information and use that info. to create the
36716 // polylines
36717
36718 // A map to store the segments associated to a boundary, non sorted
36719 std::map<unsigned, Vector<std::pair<unsigned, unsigned>>>
36720 unsorted_boundary_segments;
36721
36722 // Independent storage for the boundaries ids found in the segments so that
36723 // the polylines, and therefore polygons be created in the order they appear
36724 // in the polyfile
36725 Vector<unsigned> sorted_boundaries_ids;
36726
36727 // Process poly file to extract edges
36728 //-----------------------------------
36729
36730 // Open poly file
36731 std::ifstream poly_file(poly_file_name.c_str(), std::ios_base::in);
36732
36733 // Check that the file actually opened correctly
36734 if (!poly_file.is_open())
36735 {
36736 std::string error_msg("Failed to open poly file: ");
36737 error_msg += "\"" + poly_file_name + "\".";
36738 throw OomphLibError(
36739 error_msg, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36740 }
36741
36742 // Number of nodes in poly file --- these will be ignore
36743 unsigned n_node_poly;
36744 poly_file >> n_node_poly;
36745
36746 // Dimension
36747 poly_file >> dimension;
36748
36749 // Attribute flag
36750 unsigned attribute_flag;
36751 poly_file >> attribute_flag;
36752
36753 // Flag for boundary markers
36754 poly_file >> boundary_markers_flag;
36755
36756 // Ignore node information: Note: No, we can't extract the
36757 // actual nodes themselves from here!
36758 unsigned dummy;
36759 for (unsigned i = 0; i < n_node_poly; i++)
36760 {
36761 // Read in (and discard) node number and x and y coordinates
36762 poly_file >> dummy;
36763 poly_file >> dummy;
36764 poly_file >> dummy;
36765 // read in the attributes
36766 for (unsigned j = 0; j < attribute_flag; ++j)
36767 {
36768 poly_file >> dummy;
36769 }
36770 // read in the boundary marker
36771 if (boundary_markers_flag == 1)
36772 {
36773 poly_file >> dummy;
36774 }
36775 }
36776
36777 // Variable used to read the values from the input file
36778 unsigned read_value;
36779
36780 // Number of segments
36781 poly_file >> read_value;
36782 const unsigned nglobal_segments = read_value;
36783
36784 // Boundary marker flag
36785 poly_file >> boundary_markers_flag;
36786
36787 // Global segment number
36788 unsigned global_segment_number;
36789
36790 // Node identifier set (used to identify possible internal boundaries)
36791 std::set<unsigned> nodes_ids;
36792
36793 // Extract information for each segment
36794 for (unsigned i = 0; i < nglobal_segments; i++)
36795 {
36796 // Node id on the edge of the segment
36797 unsigned lnode_id = 0; // left node
36798 unsigned rnode_id = 0; // right node
36799 unsigned bnd_id = 0; // boundary id associated to the current segment
36800 poly_file >> global_segment_number;
36801 poly_file >> lnode_id;
36802 poly_file >> rnode_id;
36803 nodes_ids.insert(lnode_id);
36804 nodes_ids.insert(rnode_id);
36805 if (boundary_markers_flag)
36806 {
36807 poly_file >> bnd_id;
36808 }
36809
36810 // Store the segments info. (use bnd_id - 1 because the nodes and
36811 // elements associated the bnd_id have been associated by external
36812 // methods to bnd_id - 1)
36813 unsorted_boundary_segments[bnd_id - 1].push_back(
36814 std::make_pair(lnode_id, rnode_id));
36815
36816 // Add the boundary id to the vector of boundaries ids only if it
36817 // has not been added, the polylines will be created using this
36818 // order
36819
36820 // Get the number of boundaries ids currently sorted
36821 const unsigned nsorted_boundaries_ids = sorted_boundaries_ids.size();
36822 // Flag to know if the boundary id was found
36823 bool boundary_id_found = false;
36824 for (unsigned ib = 0; ib < nsorted_boundaries_ids; ib++)
36825 {
36826 if (sorted_boundaries_ids[ib] == bnd_id - 1)
36827 {
36828 boundary_id_found = true;
36829 break;
36830 } // if (sorted_boundaries_ids[ib] == bnd_id - 1)
36831 } // for (ib < nsorted_boundaries_ids)
36832
36833 // If th boundary id has not been added, then add it!!!
36834 if (!boundary_id_found)
36835 {
36836 sorted_boundaries_ids.push_back(bnd_id - 1);
36837 } // if (!boundary_id_found)
36838 }
36839
36840 // Verify if there are internal boundaries defined, if that is the
36841 // case we can not continue since we are not yet supporting internal
36842 // boundaries defined in polyfiles to created a mesh that may be
36843 // adapted
36844#ifdef PARANOID
36845 if (nglobal_segments != nodes_ids.size())
36846 {
36847 std::ostringstream error_message;
36848 error_message
36849 << "The number of nodes (" << nodes_ids.size() << ") and segments ("
36850 << nglobal_segments << ") is different.\nThis may mean that there "
36851 << "are internal non-closed boundaries defined in\nthe polyfile. "
36852 << "If you need this feature please use the TriangleMeshPoyLine\n"
36853 << "and TriangleMeshCurviLine objects to define your domain.\n\n";
36854 throw OomphLibError(
36855 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36856 }
36857#endif
36858
36859 // Now sort the segments associated to a boundary to create a contiguous
36860 // polyline, but first check that the number of found boundaries be the
36861 // same as the current number of boundaries in the mesh
36862 const unsigned nboundary = unsorted_boundary_segments.size();
36863
36864#ifdef PARANOID
36865 if (nboundary != this->nboundary())
36866 {
36867 std::ostringstream error_message;
36868 error_message
36869 << "The number of boundaries on the mesh (" << this->nboundary()
36870 << ") is different from the number of\nboundaries read from the "
36871 << "polyfiles (" << unsorted_boundary_segments.size() << ")!!!\n\n\n";
36872 throw OomphLibError(
36873 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36874 }
36875#endif
36876
36877 // Get the number of sorted boundaries ids and check that it matches
36878 // with the total number of boundaries
36879 const unsigned nsorted_boundaries_ids = sorted_boundaries_ids.size();
36880#ifdef PARANOID
36881 if (nsorted_boundaries_ids != this->nboundary())
36882 {
36883 std::ostringstream error_message;
36884 error_message
36885 << "The number of boundaries on the mesh (" << this->nboundary()
36886 << ") is different from the number of\nsorted boundaries ids read "
36887 << "from the polyfiles (" << nsorted_boundaries_ids << ")!!!\n\n\n";
36888 throw OomphLibError(
36889 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36890 }
36891#endif
36892
36893 // Sorted segments (to create a polyline -- boundary)
36894 std::map<unsigned, std::list<unsigned>> sorted_boundary_segments;
36895
36896 // Go through all the found boundaries
36897 std::map<unsigned, Vector<std::pair<unsigned, unsigned>>>::iterator it;
36898
36899 for (it = unsorted_boundary_segments.begin();
36900 it != unsorted_boundary_segments.end();
36901 it++)
36902 {
36903 // Get the current boundary id, only look for the segments
36904 // associated with this boundary
36905 const unsigned bnd_id = (*it).first;
36906 Vector<std::pair<unsigned, unsigned>> segments_edges = (*it).second;
36907
36908 // Now sort the segments associated to this boundary
36909 std::map<std::pair<unsigned, unsigned>, bool> segment_done;
36910 const unsigned nsegments = segments_edges.size();
36911
36912 // Sorted nodes for the current segment
36913 std::list<unsigned> sorted_segments;
36914
36915 // Get the left and right node of the zero segment
36916 unsigned left_node_id = segments_edges[0].first;
36917 unsigned right_node_id = segments_edges[0].second;
36918
36919 // ... and add it to the sorted segments structure
36920 sorted_segments.push_back(left_node_id);
36921 sorted_segments.push_back(right_node_id);
36922
36923 // Mark the current segment as done
36924 segment_done[segments_edges[0]] = true;
36925
36926 // Set the number of sorted segments
36927 unsigned nsorted_segments = 1;
36928
36929 while (nsorted_segments < nsegments)
36930 {
36931 for (unsigned i = 1; i < nsegments; i++)
36932 {
36933 // Check if the i-th segments has been done
36934 if (!segment_done[segments_edges[i]])
36935 {
36936 // Get the left and right node id
36937 unsigned current_left_node_id = segments_edges[i].first;
36938 unsigned current_right_node_id = segments_edges[i].second;
36939
36940 // Now check if the current segment can be added to the left
36941 // or right side of the sorted segments
36942 if (current_left_node_id == right_node_id)
36943 {
36944 // Add the current_right_node_id to the right of the sorted
36945 // segments
36946 sorted_segments.push_back(current_right_node_id);
36947 // Increase the number of sorted segments
36948 nsorted_segments++;
36949 // Mark the segment as done
36950 segment_done[segments_edges[i]] = true;
36951 // Update the right most node
36952 right_node_id = current_right_node_id;
36953 // Break the for loop
36954 break;
36955 }
36956 else if (current_right_node_id == left_node_id)
36957 {
36958 // Add the current_left_node_id to the left of the sorted
36959 // segments
36960 sorted_segments.push_front(current_left_node_id);
36961 // Increase the number of sorted segments
36962 nsorted_segments++;
36963 // Mark the segment as done
36964 segment_done[segments_edges[i]] = true;
36965 // Update the left most node
36966 left_node_id = current_left_node_id;
36967 // Break the for loop
36968 break;
36969 }
36970 else if (current_left_node_id == left_node_id)
36971 {
36972 // Add the current_right_node_id to the left of the sorted
36973 // segments
36974 sorted_segments.push_front(current_right_node_id);
36975 // Increase the number of sorted segments
36976 nsorted_segments++;
36977 // Mark the segment as done
36978 segment_done[segments_edges[i]] = true;
36979 // Update the left most node
36980 left_node_id = current_right_node_id;
36981 // Break the for loop
36982 break;
36983 }
36984 else if (current_right_node_id == right_node_id)
36985 {
36986 // Add the current_left_node_id to the right of the sorted
36987 // segments
36988 sorted_segments.push_back(current_left_node_id);
36989 // Increase the number of sorted segments
36990 nsorted_segments++;
36991 // Mark the segment as done
36992 segment_done[segments_edges[i]] = true;
36993 // Update the left most node
36994 right_node_id = current_left_node_id;
36995 // Break the for loop
36996 break;
36997 }
36998 } // if (!segment_done[segments_edges[i]])
36999 } // for (i < nsegments)
37000 } // while(nsorted_segments < nsegments)
37001
37002 sorted_boundary_segments[bnd_id] = sorted_segments;
37003
37004 } // for (unsorted_boundary_segments.begin();
37005 // unsorted_boundary_segments.end())
37006
37007#ifdef PARANOID
37008 if (sorted_boundary_segments.size() != this->nboundary())
37009 {
37010 std::ostringstream error_message;
37011 error_message
37012 << "The number of boundaries on the mesh (" << this->nboundary()
37013 << ") is different from the number\nof sorted boundaries to create the "
37014 << "polylines (" << sorted_boundary_segments.size() << ")\n\n";
37015 throw OomphLibError(
37016 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
37017 }
37018#endif
37019
37020 // Now we have the sorted nodes, we can create the polylines by
37021 // getting the vertices of the nodes
37022 Vector<TriangleMeshPolyLine*> polylines_pt(nboundary);
37023 unsigned current_polyline = 0;
37024
37025 // Go through the sorted boundaries using the sorted boundaries ids
37026 for (unsigned ib = 0; ib < nsorted_boundaries_ids; ib++)
37027 {
37028 // Get the boundary id from the vector of sorted boundaries ids
37029 const unsigned bnd_id = sorted_boundaries_ids[ib];
37030
37031 // Create a vector representation for ease to use
37032 // Get the vertices of the nodes that create the boundary / polyline
37033 Vector<unsigned> nodes_ids;
37034 for (std::list<unsigned>::iterator it_list =
37035 sorted_boundary_segments[bnd_id].begin();
37036 it_list != sorted_boundary_segments[bnd_id].end();
37037 it_list++)
37038 {
37039 nodes_ids.push_back((*it_list));
37040 }
37041
37042 // Get the number of vertices for the polyline
37043 const unsigned nvertices = nodes_ids.size();
37044
37045 // The storage for the vertices
37046 Vector<Vector<double>> vertices(nvertices);
37047
37048 // Now get the vertices of the nodes of the current boundary
37049 for (unsigned i = 0; i < nvertices; i++)
37050 {
37051 // Get the vertices
37052 vertices[i].resize(2);
37053 vertices[i][0] = x_node[nodes_ids[i] - 1];
37054 vertices[i][1] = y_node[nodes_ids[i] - 1];
37055 }
37056
37057 // Now create the polyline
37058
37059 // Note: The bnd_id is the real bnd_id (from the input file) - 1
37060 // since nodes and elements of the current boundary have been
37061 // associated to bnd_id - 1)
37062 polylines_pt[current_polyline] =
37063 new TriangleMeshPolyLine(vertices, bnd_id);
37064
37065 // Updates bnd_id<--->curve section map
37066 this->Boundary_curve_section_pt[bnd_id] =
37067 dynamic_cast<TriangleMeshCurveSection*>(polylines_pt[current_polyline]);
37068
37069 // Increase the index for the polyline storage
37070 current_polyline++;
37071
37072 } // for (it_sorted = sorted_boundary_segments.begin();
37073 // it_sorted != sorted_boundary_segments.end())
37074
37075 // Now create the polygons or closed curves
37076 // Sort the polylines to create polygons
37077 unsigned nsorted_polylines = 0;
37078
37079 // Number of created polygons
37080 unsigned npolygons = 0;
37081
37082 // Storage for the polygons
37083 Vector<TriangleMeshPolygon*> polygons_pt;
37084
37085 // Mark the already done polylines
37086 std::map<unsigned, bool> polyline_done;
37087 while (nsorted_polylines < nboundary)
37088 {
37089 // Storage for the curve sections that create a polygon
37090 std::list<TriangleMeshCurveSection*> sorted_curve_sections_pt;
37091
37092 unsigned init_poly = 0;
37093#ifdef PARANOID
37094 bool found_root_polyline = false;
37095#endif
37096 // Get the left and right node of the current polyline
37097 for (unsigned i = 0; i < nboundary; i++)
37098 {
37099 if (!polyline_done[i])
37100 {
37101 init_poly = i;
37102 // Increase the number of sorted polylines
37103 nsorted_polylines++;
37104#ifdef PARANOID
37105 // Mark as found the root polyline
37106 found_root_polyline = true;
37107#endif
37108 // Mark the polyline as done
37109 polyline_done[i] = true;
37110 // Add the polyline to the curve sections storage
37111 sorted_curve_sections_pt.push_back(polylines_pt[i]);
37112 // Break the loop to set we have found a root polyline
37113 break;
37114 }
37115 }
37116
37117#ifdef PARANOID
37118 if (!found_root_polyline)
37119 {
37120 std::ostringstream error_message;
37121 error_message << "Was not possible to found the root polyline to "
37122 "create polygons\n\n";
37123 throw OomphLibError(error_message.str(),
37124 OOMPH_CURRENT_FUNCTION,
37125 OOMPH_EXCEPTION_LOCATION);
37126 }
37127#endif
37128
37129 // Get the associated boundary to the current polyline
37130 const unsigned bnd_id = polylines_pt[init_poly]->boundary_id();
37131 // Get the initial and final node id of the current polyline
37132 unsigned left_node_id = sorted_boundary_segments[bnd_id].front();
37133 unsigned right_node_id = sorted_boundary_segments[bnd_id].back();
37134
37135 // Flag to know that we already have a closed polygon
37136 bool closed_polygon = false;
37137
37138 do
37139 {
37140 // Go through all the polylines
37141 for (unsigned i = init_poly; i < nboundary; i++)
37142 {
37143 // Check that the polyline has not been currently done
37144 if (!polyline_done[i])
37145 {
37146 // Get the initial and final nodes id of the current polyline
37147
37148 // Get the associated boundary to the current polyline
37149 const unsigned cbnd_id = polylines_pt[i]->boundary_id();
37150 // Get the initial and final node id of the current polyline
37151 unsigned cleft_node_id = sorted_boundary_segments[cbnd_id].front();
37152 unsigned cright_node_id = sorted_boundary_segments[cbnd_id].back();
37153
37154 // Check if the polyline goes to the left or right of the
37155 // current sorted polylines
37156 if (cleft_node_id == right_node_id)
37157 {
37158 // Add the polyline to the curve section storage
37159 sorted_curve_sections_pt.push_back(polylines_pt[i]);
37160 // Mark the polyline as done
37161 polyline_done[i] = true;
37162 // Update the right node
37163 right_node_id = cright_node_id;
37164 // Increase the number of done polyines
37165 nsorted_polylines++;
37166 // Break the for loop
37167 break;
37168 }
37169 else if (cright_node_id == left_node_id)
37170 {
37171 // Add the polyline to the curve section storage
37172 sorted_curve_sections_pt.push_front(polylines_pt[i]);
37173 // Mark the polyline as done
37174 polyline_done[i] = true;
37175 // Update the right node
37176 left_node_id = cleft_node_id;
37177 // Increase the number of done polyines
37178 nsorted_polylines++;
37179 // Break the for loop
37180 break;
37181 }
37182 else if (cleft_node_id == left_node_id)
37183 {
37184 // First reverse the polyline
37185 polylines_pt[i]->reverse();
37186 // Add the polyline to the curve section storage
37187 sorted_curve_sections_pt.push_front(polylines_pt[i]);
37188 // Mark the polyline as done
37189 polyline_done[i] = true;
37190 // Update the right node
37191 left_node_id = cright_node_id;
37192 // Increase the number of done polyines
37193 nsorted_polylines++;
37194 // Break the for loop
37195 break;
37196 }
37197 else if (cright_node_id == right_node_id)
37198 {
37199 // First reverse the polyline
37200 polylines_pt[i]->reverse();
37201 // Add the polyline to the curve section storage
37202 sorted_curve_sections_pt.push_back(polylines_pt[i]);
37203 // Mark the polyline as done
37204 polyline_done[i] = true;
37205 // Update the right node
37206 right_node_id = cleft_node_id;
37207 // Increase the number of done polyines
37208 nsorted_polylines++;
37209 // Break the for loop
37210 break;
37211 }
37212 } // if (!polyline_done[i])
37213
37214 } // for (i < nboundary)
37215
37216 // We have created a polygon
37217 if (left_node_id == right_node_id)
37218 {
37219 // Set the flag as true
37220 closed_polygon = true;
37221 }
37222
37223 } while (nsorted_polylines < nboundary && !closed_polygon);
37224
37225#ifdef PARANOID
37226 if (!closed_polygon)
37227 {
37228 std::ostringstream error_message;
37229 error_message
37230 << "It was not possible to create a closed curve, these are the "
37231 << "vertices of the already sorted polylines\n\n";
37232 unsigned cpolyline = 0;
37233 for (std::list<TriangleMeshCurveSection*>::iterator it_list =
37234 sorted_curve_sections_pt.begin();
37235 it_list != sorted_curve_sections_pt.end();
37236 it_list++)
37237 {
37238 error_message << "Polyline (" << cpolyline << ")\n";
37239 TriangleMeshPolyLine* tmp_poly_pt =
37240 dynamic_cast<TriangleMeshPolyLine*>((*it_list));
37241 const unsigned nvertex = tmp_poly_pt->nvertex();
37242 for (unsigned v = 0; v < nvertex; v++)
37243 {
37244 error_message << "(" << tmp_poly_pt->vertex_coordinate(v)[0] << ", "
37245 << tmp_poly_pt->vertex_coordinate(v)[1] << ")\n";
37246 }
37247 error_message << "\n";
37248 cpolyline++;
37249 }
37250 throw OomphLibError(error_message.str(),
37251 OOMPH_CURRENT_FUNCTION,
37252 OOMPH_EXCEPTION_LOCATION);
37253 }
37254#endif
37255
37256 // Create a vector version to create the polygon from the sorted
37257 // polyines
37258 Vector<TriangleMeshCurveSection*> tmp_sorted_curve_sections_pt;
37259 for (std::list<TriangleMeshCurveSection*>::iterator it_list =
37260 sorted_curve_sections_pt.begin();
37261 it_list != sorted_curve_sections_pt.end();
37262 it_list++)
37263 {
37264 tmp_sorted_curve_sections_pt.push_back((*it_list));
37265 }
37266
37267 // Create a new polygon by using the new created polylines
37268 TriangleMeshPolygon* polygon_pt =
37269 new TriangleMeshPolygon(tmp_sorted_curve_sections_pt);
37270
37271 // Keep track of new created polygons that need to be deleted!!!
37272 this->Free_polygon_pt.insert(polygon_pt);
37273
37274 // Store the polygon in the polygons storages
37275 polygons_pt.push_back(polygon_pt);
37276
37277 npolygons++;
37278
37279 } // while(nsorted_polylines < nboundary)
37280
37281 // ------------------------------------------------------------------
37282 // Before filling the data structures we need to identify the outer
37283 // closed boundary and the inner closed boundaries.
37284 // If the nodes are not in order we throw a warning message
37285
37286 // Index for the polygon that is currently considered as the outer
37287 // boundary
37288 unsigned index_outer = 0;
37289
37290 for (unsigned idx_outer = 0; idx_outer < npolygons; idx_outer++)
37291 {
37292 // Get the vertices of the outer boundary
37293 Vector<Vector<double>> outer_vertex_coordinates;
37294
37295 // Flag to know if ALL the inner closed boundaries are inside the
37296 // outer closed boundary
37297 bool all_inner_inside = true;
37298
37299 // Number of polylines of the outer boundary
37300 const unsigned nouter_polylines = polygons_pt[idx_outer]->npolyline();
37301 for (unsigned p = 0; p < nouter_polylines; p++)
37302 {
37303 TriangleMeshPolyLine* tmp_poly_pt =
37304 polygons_pt[idx_outer]->polyline_pt(p);
37305 const unsigned nvertex = tmp_poly_pt->nvertex();
37306 for (unsigned v = 0; v < nvertex; v++)
37307 {
37308 Vector<double> current_vertex = tmp_poly_pt->vertex_coordinate(v);
37309 outer_vertex_coordinates.push_back(current_vertex);
37310 } // for (v < nvertex)
37311 } // for (p < nouter_polylines)
37312
37313 // Now get the vertices for the inner boundaries
37314
37315 // First get the number of inner closed boundaries (polygons size
37316 // minus one because one of the polygons is considered to be the
37317 // outer closed boundary
37318 const unsigned ninner_polygons = polygons_pt.size() - 1;
37319
37320 // Store the vertices of the inner closed boundaries
37321 Vector<Vector<Vector<double>>> inner_vertex_coordinates(ninner_polygons);
37322 // Get all the vertices of the inner closed boundaries
37323 for (unsigned i = 0; i <= ninner_polygons; i++)
37324 {
37325 if (i != idx_outer)
37326 {
37327 // Number of polylines of the current internal closed boundary
37328 const unsigned ninner_polylines = polygons_pt[i]->npolyline();
37329 for (unsigned p = 0; p < ninner_polylines; p++)
37330 {
37331 TriangleMeshPolyLine* tmp_poly_pt = polygons_pt[i]->polyline_pt(p);
37332 const unsigned nvertex = tmp_poly_pt->nvertex();
37333 for (unsigned v = 0; v < nvertex; v++)
37334 {
37335 Vector<double> current_vertex = tmp_poly_pt->vertex_coordinate(v);
37336 if (i < idx_outer)
37337 {
37338 inner_vertex_coordinates[i].push_back(current_vertex);
37339 }
37340 else if (i > idx_outer)
37341 {
37342 inner_vertex_coordinates[i - 1].push_back(current_vertex);
37343 }
37344 } // for (v < nvertex)
37345
37346 } // for (p < ninner_polylines)
37347
37348 } // if (i != index_outer)
37349
37350 } // for (i <= ninner_polygons)
37351
37352 // Now check that ALL the vertices of ALL the internal closed
37353 // boundaries are inside the outer closed boundary
37354 for (unsigned i = 0; i < ninner_polygons; i++)
37355 {
37356 // Get the number of vertices in the current internal closed
37357 // boundary
37358 const unsigned nvertex_internal = inner_vertex_coordinates[i].size();
37359 for (unsigned v = 0; v < nvertex_internal; v++)
37360 {
37361 // Get a vertex in the current internal closed boundary
37362 Vector<double> current_point = inner_vertex_coordinates[i][v];
37363 all_inner_inside &= this->is_point_inside_polygon_helper(
37364 outer_vertex_coordinates, current_point);
37365
37366 // Check if we should continue checking for more points inside
37367 // the current proposed outer boundary
37368 if (!all_inner_inside)
37369 {
37370 // Break the "for" for the vertices
37371 break;
37372 }
37373
37374 } // for (v < nvertex_internal)
37375
37376 // Check if we should continue checking for more inner closed
37377 // boundaries inside the current proposed outer boundary
37378 if (!all_inner_inside)
37379 {
37380 // Break the "for" for the inner boundaries
37381 break;
37382 }
37383
37384 } // for (i < ninner_polygons)
37385
37386 // Check if all the vertices of all the polygones are inside the
37387 // current proposed outer boundary
37388 if (all_inner_inside)
37389 {
37390 index_outer = idx_outer;
37391 break;
37392 }
37393
37394 } // for (idx_outer < npolygons)
37395
37396#ifdef PARANOID
37397 // Check if the first nodes listed in the polyfiles correspond to
37398 // the outer boundary, if that is not the case then throw a warning
37399 // message
37400 if (index_outer != 0)
37401 {
37402 std::ostringstream warning_message;
37403 warning_message
37404 << "The first set of nodes listed in the input polyfiles does not\n"
37405 << "correspond to the outer closed boundary. This may lead to\n"
37406 << "problems at the adaptation stage if the holes coordinates\n"
37407 << "are no correctly associated to the inner closed boundaries.\n"
37408 << "You can check the generated mesh by calling the output() method\n"
37409 << "from the mesh object '(problem.mesh_pt()->output(string))'\n\n";
37410 OomphLibWarning(warning_message.str(),
37411 OOMPH_CURRENT_FUNCTION,
37412 OOMPH_EXCEPTION_LOCATION);
37413 } // if (index_outer != 0)
37414#endif
37415
37416 // ------------------------------------------------------------------
37417 // Now fill the data structures
37418
37419 // Store outer polygon
37420 // We are assuming there is only one outer polygon
37421 this->Outer_boundary_pt.resize(1);
37422 this->Outer_boundary_pt[0] = polygons_pt[index_outer];
37423
37424 this->Internal_polygon_pt.resize(npolygons - 1);
37425 for (unsigned i = 0; i < npolygons; i++)
37426 {
37427 if (i != index_outer)
37428 {
37429 if (i < index_outer)
37430 {
37431 // Store internal polygons by copy constructor
37432 this->Internal_polygon_pt[i] = polygons_pt[i];
37433 }
37434 else if (i > index_outer)
37435 {
37436 // Store internal polygons by copy constructor
37437 this->Internal_polygon_pt[i - 1] = polygons_pt[i];
37438 }
37439 } // if (i != index_outer)
37440 } // for (i < npolygons)
37441
37442 // Before assigning the hole vertex coordinate to the inner closed
37443 // boundaries check that the holes are listed in orderm if that is
37444 // not the case the associate each hole vertex coordinate to the
37445 // inner closed boundaries
37446
37447 // Store the vertices of the inner closed boundaries
37448 Vector<Vector<Vector<double>>> inner_vertex_coordinates(npolygons - 1);
37449 // Get all the vertices of the inner closed boundaries
37450 for (unsigned i = 0; i < npolygons - 1; i++)
37451 {
37452 // Number of polylines of the current internal closed boundary
37453 const unsigned ninner_polylines =
37454 this->Internal_polygon_pt[i]->npolyline();
37455 for (unsigned p = 0; p < ninner_polylines; p++)
37456 {
37457 TriangleMeshPolyLine* tmp_poly_pt =
37458 this->Internal_polygon_pt[i]->polyline_pt(p);
37459 // Number of vertices of the current polyline in the current
37460 // internal closed polygon
37461 const unsigned nvertex = tmp_poly_pt->nvertex();
37462 for (unsigned v = 0; v < nvertex; v++)
37463 {
37464 Vector<double> current_vertex = tmp_poly_pt->vertex_coordinate(v);
37465 inner_vertex_coordinates[i].push_back(current_vertex);
37466 } // for (v < nvertex)
37467
37468 } // for (p < ninner_polylines)
37469
37470 } // for (i <= ninner_polygons)
37471
37472 // Holes information
37473 unsigned nholes;
37474 poly_file >> nholes;
37475
37476#ifdef PARANOID
37477 if (npolygons > 1 && (npolygons - 1) != nholes)
37478 {
37479 std::ostringstream error_message;
37480 error_message
37481 << "The number of holes (" << nholes << ") does not correspond "
37482 << "with the number\nof internal polygons (" << npolygons - 1 << ")\n\n"
37483 << "Using polyfiles as input does not currently allows the\n"
37484 << "definition of more than one outer polygon\n\n";
37485 throw OomphLibError(
37486 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
37487 }
37488#endif
37489
37490 // Storage for the holes
37491 Vector<Vector<double>> hole_coordinates(nholes);
37492
37493 // Dummy for hole number
37494 unsigned dummy_hole;
37495 // Loop over the holes to get centre coords
37496 for (unsigned ihole = 0; ihole < nholes; ihole++)
37497 {
37498 hole_coordinates[ihole].resize(2);
37499 // Read the centre value
37500 poly_file >> dummy_hole;
37501 poly_file >> hole_coordinates[ihole][0];
37502 poly_file >> hole_coordinates[ihole][1];
37503 }
37504
37505 // Vector that store the index of the hole coordinate that
37506 // correspond to each internal closed polygon
37507 Vector<unsigned> index_hole_of_internal_polygon(npolygons - 1);
37508 std::map<unsigned, bool> hole_done;
37509
37510 // Now associate each hole vertex to a corresponding internal closed
37511 // polygon
37512 for (unsigned i = 0; i < npolygons - 1; i++)
37513 {
37514 // Find which hole is associated to each internal closed boundary
37515 for (unsigned h = 0; h < nholes; h++)
37516 {
37517 // If the hole has not been previously associated
37518 if (!hole_done[h])
37519 {
37520 // Get the hole coordinate
37521 Vector<double> current_point = hole_coordinates[h];
37522
37523 const bool hole_in_polygon = this->is_point_inside_polygon_helper(
37524 inner_vertex_coordinates[i], current_point);
37525
37526 // If the hole is inside the polygon
37527 if (hole_in_polygon)
37528 {
37529 // Mark the hole as done
37530 hole_done[h] = true;
37531 // Associate the current hole with the current inner closed
37532 // boundary
37533 index_hole_of_internal_polygon[i] = h;
37534 // Break the search
37535 break;
37536 }
37537
37538 } // if (!hole_done[h])
37539
37540 } // for (h < nholes)
37541
37542 } // for (i < npolygons-1)
37543
37544#ifdef PARANOID
37545 if (hole_done.size() != npolygons - 1)
37546 {
37547 std::ostringstream error_message;
37548 error_message
37549 << "Not all the holes were associated to an internal closed boundary\n"
37550 << "Only (" << hole_done.size()
37551 << ") holes were assigned for a total of\n"
37552 << "(" << npolygons - 1 << ") internal closed boundaries.\n"
37553 << "You can check the generated mesh by calling the output() method\n"
37554 << "from the mesh object '(problem.mesh_pt()->output(string))'\n\n";
37555 throw OomphLibError(
37556 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
37557 } // if (index_hole != ihole)
37558#endif
37559
37560 // Assign the holes coordinates to the internal polygons
37561 for (unsigned ihole = 0; ihole < nholes; ihole++)
37562 {
37563 // Get the index hole of the current internal closed polygon
37564 const unsigned index_hole = index_hole_of_internal_polygon[ihole];
37565#ifdef PARANOID
37566 // Check if the hole index is the same as the internal closed
37567 // boundary, it means that the holes were listed in the same order
37568 // as the nodes of the internal closed boundaries
37569 if (index_hole != ihole)
37570 {
37571 std::ostringstream error_message;
37572 error_message
37573 << "The hole vertices coordinates are not listed in the same order\n"
37574 << "as the nodes that define the internal closed boundaries.\n"
37575 << "This may lead to problems in case that the holes coordinates\n"
37576 << "were no properly assigned to the internal closed boundaries.\n"
37577 << "You can check the generated mesh by calling the output() method\n"
37578 << "from the mesh object '(problem.mesh_pt()->output(string))'\n\n";
37579 throw OomphLibError(error_message.str(),
37580 OOMPH_CURRENT_FUNCTION,
37581 OOMPH_EXCEPTION_LOCATION);
37582 } // if (index_hole != ihole)
37583#endif
37584
37585 // Set the hole coordinate for the internal polygon
37586 this->Internal_polygon_pt[ihole]->internal_point() =
37587 hole_coordinates[index_hole];
37588 }
37589
37590 // Ignore the first line with structure description
37591 poly_file.ignore(80, '\n');
37592
37593 // Regions information
37594 unsigned nregions;
37595
37596 // Extract regions information
37597 // But first check if there are regions or not
37598 std::string regions_info_string;
37599
37600 // Read line up to termination sign
37601 getline(poly_file, regions_info_string);
37602
37603 // Check if the read string is a number or a comment wrote by triangle,
37604 // if it is a number then that is the number of regions
37605 if (isdigit(regions_info_string.c_str()[0]))
37606 {
37607 nregions = std::atoi(regions_info_string.c_str());
37608 }
37609 else
37610 {
37611 nregions = 0;
37612 }
37613
37614 // The regions coordinates
37615 std::map<unsigned, Vector<double>> regions_coordinates;
37616
37617 // Dummy for regions number
37618 unsigned dummy_region;
37619
37620 unsigned region_id;
37621
37622 // Loop over the regions to get their coords
37623 for (unsigned iregion = 0; iregion < nregions; iregion++)
37624 {
37625 Vector<double> tmp_region_coordinates(2);
37626 // Read the regions coordinates
37627 poly_file >> dummy_region;
37628 poly_file >> tmp_region_coordinates[0];
37629 poly_file >> tmp_region_coordinates[1];
37630 poly_file >> region_id;
37631 regions_coordinates[region_id].resize(2);
37632 regions_coordinates[region_id][0] = tmp_region_coordinates[0];
37633 regions_coordinates[region_id][1] = tmp_region_coordinates[1];
37634
37635 // Ignore the first line with structure description
37636 poly_file.ignore(80, '\n');
37637
37638 // Verify if not using the default region number (zero)
37639 if (region_id == 0)
37640 {
37641 std::ostringstream error_message;
37642 error_message
37643 << "Please use another region id different from zero.\n"
37644 << "It is internally used as the default region number.\n";
37645 throw OomphLibError(error_message.str(),
37646 OOMPH_CURRENT_FUNCTION,
37647 OOMPH_EXCEPTION_LOCATION);
37648 }
37649 }
37650
37651 // Store the extra regions coordinates
37652 this->Regions_coordinates = regions_coordinates;
37653
37654 poly_file.close();
37655 }
37656
37657 //======================================================================
37658 /// Updates the polygon but using the elements area instead of
37659 /// the default refinement and unrefinement methods
37660 //======================================================================
37661 template<class ELEMENT>
37663 TriangleMeshPolygon*& polygon_pt, const Vector<double>& target_area)
37664 {
37665 // Verify that there was a change on the polygon representation
37666 unsigned update_was_performed = false;
37667
37668 const unsigned nele = this->nelement();
37669
37670 // - Get the vertices along the boundaries and for each element identify
37671 // its associated target error.
37672 // - Get face mesh representation of each polyline.
37673 // - Get the vertices with the help of face elements.
37674 // - Find the global index in the mesh of the face element and use
37675 // it to get its associated target area
37676
37677 // Get the face mesh representation
37678 Vector<Mesh*> face_mesh_pt;
37679 get_face_mesh_representation(polygon_pt, face_mesh_pt);
37680
37681 // Create vertices of the polylines by using the vertices of the
37682 // FaceElements
37683 Vector<double> vertex_coord(3); // zeta,x,y
37684 Vector<double> bound_left(1);
37685 Vector<double> bound_right(1);
37686
37687 unsigned n_polyline = polygon_pt->npolyline();
37688
37689 // Go for each polyline
37690 for (unsigned p = 0; p < n_polyline; p++)
37691 {
37692 // Get the MeshAsGeomObject representation just once per polyline,
37693 // this object is only used by the
37694 // refine_boundary_constrained_by_target_area() method. We get it
37695 // here to ensure that all processors (in a distributed context)
37696 // get this representation just once, and because an AllToAll MPI
37697 // communication is used in this calling
37698 MeshAsGeomObject* mesh_geom_obj_pt =
37699 new MeshAsGeomObject(face_mesh_pt[p]);
37700
37701 // Set of coordinates on the boundary
37702 // Set entries are ordered on first entry in vector which stores
37703 // the boundary coordinate so the vertices come out in order!
37704 std::set<Vector<double>> vertex_nodes;
37705
37706 // Vector to store the vertices, transfer the sorted vertices from the
37707 // set to this vector, --- including the z-value ---
37708 Vector<Vector<double>> tmp_vector_vertex_node;
37709
37710 // Vector to store the coordinates of the polylines, same as the
37711 // tmp_vector_vertex_node vector (after adding more nodes) but
37712 // --- without the z-value ---, used to re-generate the polylines
37713 Vector<Vector<double>> vector_vertex_node;
37714
37715#ifdef OOMPH_HAS_MPI
37716 // --------- Stuff to deal with splitted boundaries ---------- Begin -----
37717 // Set of coordinates that are on the boundary (splitted boundary version)
37718 // The first vector is used to allocate the points for each sub-boundary
37719 // Set entries are ordered on first entry in vector which stores
37720 // the boundary coordinate so the vertices come out in order!
37721 Vector<std::set<Vector<double>>> sub_vertex_nodes;
37722
37723 // Vector to store the vertices, transfer the sorted vertices from the
37724 // set (sub_vertex_nodes) to this vector, --- including the z-value ---
37725 Vector<Vector<Vector<double>>> sub_tmp_vector_vertex_node;
37726
37727 // Vector to store the coordinates of the polylines that will represent
37728 // the splitted boundary. Used to pass the info. from sub_vertex_nodes
37729 // but --- without the z-value ---, used to generate the sub-polylines
37730 Vector<Vector<Vector<double>>> sub_vector_vertex_node;
37731 // --------- Stuff to deal with splitted boundaries ----------- End ------
37732#endif
37733
37734 // Get the boundary id
37735 const unsigned bound = polygon_pt->curve_section_pt(p)->boundary_id();
37736
37737 // Get the chunk number
37738 const unsigned chunk = polygon_pt->curve_section_pt(p)->boundary_chunk();
37739
37740 /// Use a vector of vector for vertices and target areas to deal
37741 /// with the cases when the boundaries are split by the
37742 /// distribution process
37743
37744 // Loop over the face elements (ordered) and add their vertices
37745 const unsigned nface_element = face_mesh_pt[p]->nelement();
37746
37747 // Store the non halo face elements, the ones from which we will
37748 // get the vertices
37749 Vector<FiniteElement*> non_halo_face_element_pt;
37750
37751 // Map to store the index of the face element on a boundary
37752 std::map<FiniteElement*, unsigned> face_element_index_on_boundary;
37753
37754 for (unsigned ef = 0; ef < nface_element; ++ef)
37755 {
37756 FiniteElement* ele_face_pt = face_mesh_pt[p]->finite_element_pt(ef);
37757#ifdef OOMPH_HAS_MPI
37758 // Skip the halo elements if working with a distributed mesh
37759 if (this->is_mesh_distributed() && ele_face_pt->is_halo())
37760 {
37761 continue;
37762 }
37763#endif
37764 // Add the face element to the vector
37765 non_halo_face_element_pt.push_back(ele_face_pt);
37766 face_element_index_on_boundary[ele_face_pt] = ef;
37767 }
37768
37769 // Get the number of non halo face element
37770 const unsigned nnon_halo_face_element = non_halo_face_element_pt.size();
37771
37772 // Map to know the already sorted face elements
37773 std::map<FiniteElement*, bool> face_element_done;
37774
37775 // Number of done face elements
37776 unsigned nsorted_face_elements = 0;
37777
37778#ifdef OOMPH_HAS_MPI
37779 // Counter for sub_boundaries
37780 unsigned nsub_boundaries = 0;
37781#endif // #ifdef OOMPH_HAS_MPI
37782
37783 // Continue until all the face elements have been sorted
37784 // While to deal with split boundaries cases
37785 while (nsorted_face_elements < nnon_halo_face_element)
37786 {
37787 // Get and initial face element
37788 FiniteElement* ele_face_pt = 0;
37789#ifdef PARANOID
37790 bool found_initial_face_element = false;
37791#endif
37792
37793 unsigned iface = 0;
37794 for (iface = 0; iface < nnon_halo_face_element; iface++)
37795 {
37796 ele_face_pt = non_halo_face_element_pt[iface];
37797 // If not done then take it as initial face element
37798 if (!face_element_done[ele_face_pt])
37799 {
37800#ifdef PARANOID
37801 found_initial_face_element = true;
37802#endif
37803 nsorted_face_elements++;
37804 iface++;
37805 break;
37806 }
37807 }
37808
37809#ifdef PARANOID
37810 if (!found_initial_face_element)
37811 {
37812 std::ostringstream error_message;
37813 error_message << "Could not find an initial face element for the "
37814 "current segment\n";
37815 // << "----- Possible memory leak -----\n";
37816 throw OomphLibError(
37817 error_message.str(),
37818 "RefineableTriangleMesh::update_polygon_using_elements_area()",
37819 OOMPH_EXCEPTION_LOCATION);
37820 }
37821#endif
37822
37823 // Local set of coordinates that are on the boundary
37824 // Set entries are ordered on first entry in vector which stores
37825 // the boundary coordinate so the vertices come out in order!
37826 std::set<Vector<double>> local_vertex_nodes;
37827
37828 // Vector to store the vertices, transfer the sorted vertices from the
37829 // set (local) to this vector (local), --- including the z-value ---
37830 Vector<Vector<double>> local_tmp_vector_vertex_node;
37831
37832 // Vector to store the target areas, uses the same approach as the
37833 // set for the local_vertex_nodes, ordered on first entry
37834 std::set<Vector<double>> sorted_target_areas;
37835
37836 // Vector to store the target areas, used to transfer the sorted target
37837 // areas from "local_sorted_target_areas" set
37838 Vector<double> tmp_sorted_target_areas;
37839
37840 // -----------------------------------------------------------------
37841 // Add the vertices of the initial face element to the set of
37842 // local sorted vertices
37843 // -----------------------------------------------------------------
37844 unsigned nnode = ele_face_pt->nnode();
37845 // Add the left-hand node to the set:
37846 // Boundary coordinate
37847 ele_face_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
37848 vertex_coord[0] = bound_left[0];
37849
37850 // Actual coordinates
37851 for (unsigned i = 0; i < 2; i++)
37852 {
37853 vertex_coord[i + 1] = ele_face_pt->node_pt(0)->x(i);
37854 }
37855 local_vertex_nodes.insert(vertex_coord);
37856
37857 // Add the right-hand nodes to the set:
37858 // Boundary coordinate
37859 ele_face_pt->node_pt(nnode - 1)->get_coordinates_on_boundary(
37860 bound, bound_right);
37861 vertex_coord[0] = bound_right[0];
37862
37863 // Actual coordinates
37864 for (unsigned i = 0; i < 2; i++)
37865 {
37866 vertex_coord[i + 1] = ele_face_pt->node_pt(nnode - 1)->x(i);
37867 }
37868 local_vertex_nodes.insert(vertex_coord);
37869
37870 // The initial and final node on the set
37871 Node* first_node_pt = ele_face_pt->node_pt(0);
37872 Node* last_node_pt = ele_face_pt->node_pt(nnode - 1);
37873
37874 // Mark the current face element as done
37875 face_element_done[ele_face_pt] = true;
37876
37877 // -------------------------------------------------------
37878 // Find the global index in the mesh of the face element
37879 // and use it to get its associated target area
37880 // -------------------------------------------------------
37881 // Container to store the zeta value (used as index) and
37882 // the associated target area of the element
37883 Vector<double> zeta_target_area_values(2);
37884
37885 // Use the minimum zeta value to sort the target areas
37886 // along the boundary
37887 zeta_target_area_values[0] = std::min(bound_left[0], bound_right[0]);
37888
37889 // Get the index of the face element on the current boundary
37890 unsigned ef = face_element_index_on_boundary[ele_face_pt];
37891 // Get the "ef"-th element on the boundary
37892 FiniteElement* el_pt = this->boundary_element_pt(bound, ef);
37893
37894#ifdef PARANOID
37895 bool found_global_element_index = false;
37896#endif
37897 for (unsigned eg = 0; eg < nele; eg++)
37898 {
37899 // Get the "eg-th" element
37900 FiniteElement* el_compare_pt = this->finite_element_pt(eg);
37901
37902 // Compare with the element on the boundary, if equal then
37903 // store the target area
37904 if (el_pt == el_compare_pt)
37905 {
37906 zeta_target_area_values[1] = target_area[eg];
37907#ifdef PARANOID
37908 found_global_element_index = true;
37909#endif
37910 break; // break the for (e < nele) global element
37911 } // if element_pt == element_compare_pt
37912 } // for nele (on complete mesh)
37913
37914#ifdef PARANOID
37915 if (!found_global_element_index)
37916 {
37917 std::ostringstream error_message;
37918 error_message << "The global index for the (" << ef
37919 << ")-th face element "
37920 << "on\nthe (" << bound
37921 << ")-th boundary was not found!!!";
37922 throw OomphLibError(
37923 error_message.str(),
37924 "RefineableTriangleMesh::update_polygon_using_elements_area()",
37925 OOMPH_EXCEPTION_LOCATION);
37926 }
37927#endif
37928
37929 // Add the target areas to the sorted set
37930 sorted_target_areas.insert(zeta_target_area_values);
37931 // ------------------------------------------------------------------
37932
37933 // Continue iterating if a new face element has been added to the
37934 // list
37935 bool face_element_added = false;
37936
37937 // While a new face element has been added to the set of sorted
37938 // face elements then re-iterate
37939 do
37940 {
37941 // Start from the next face elements since we have already
37942 // added the previous one as the initial face element (any
37943 // previous face element had to be added on previous
37944 // iterations)
37945 for (unsigned iiface = iface; iiface < nnon_halo_face_element;
37946 iiface++)
37947 {
37948 face_element_added = false;
37949 ele_face_pt = non_halo_face_element_pt[iiface];
37950 if (!face_element_done[ele_face_pt])
37951 {
37952 // Get each individual node to check if they are contiguous
37953 nnode = ele_face_pt->nnode();
37954 Node* left_node_pt = ele_face_pt->node_pt(0);
37955 Node* right_node_pt = ele_face_pt->node_pt(nnode - 1);
37956
37957 if (left_node_pt == first_node_pt)
37958 {
37959 first_node_pt = right_node_pt;
37960 face_element_added = true;
37961 }
37962 else if (left_node_pt == last_node_pt)
37963 {
37964 last_node_pt = right_node_pt;
37965 face_element_added = true;
37966 }
37967 else if (right_node_pt == first_node_pt)
37968 {
37969 first_node_pt = left_node_pt;
37970 face_element_added = true;
37971 }
37972 else if (right_node_pt == last_node_pt)
37973 {
37974 last_node_pt = left_node_pt;
37975 face_element_added = true;
37976 }
37977
37978 if (face_element_added)
37979 {
37980 // Add the left-hand node to the set:
37981 // Boundary coordinate
37982 left_node_pt->get_coordinates_on_boundary(bound, bound_left);
37983 vertex_coord[0] = bound_left[0];
37984
37985 // Actual coordinates
37986 for (unsigned i = 0; i < 2; i++)
37987 {
37988 vertex_coord[i + 1] = left_node_pt->x(i);
37989 }
37990 local_vertex_nodes.insert(vertex_coord);
37991
37992 // Add the right-hand nodes to the set:
37993 // Boundary coordinate
37994 right_node_pt->get_coordinates_on_boundary(bound, bound_right);
37995 vertex_coord[0] = bound_right[0];
37996
37997 // Actual coordinates
37998 for (unsigned i = 0; i < 2; i++)
37999 {
38000 vertex_coord[i + 1] = right_node_pt->x(i);
38001 }
38002 local_vertex_nodes.insert(vertex_coord);
38003
38004 // Mark as done only if one of its nodes has been
38005 // added to the list
38006 face_element_done[ele_face_pt] = true;
38007 nsorted_face_elements++;
38008
38009 // -----------------------------------------------------
38010 // Find the global index in the mesh of the face element
38011 // and use it to get its associated target area
38012 // -----------------------------------------------------
38013 // Use the minimum zeta value to sort the target areas
38014 // along the boundary
38015 zeta_target_area_values[0] =
38016 std::min(bound_left[0], bound_right[0]);
38017
38018 // Get the "ef"-th element on the boundary
38019 ef = face_element_index_on_boundary[ele_face_pt];
38020 FiniteElement* lel_pt = this->boundary_element_pt(bound, ef);
38021
38022#ifdef PARANOID
38023 found_global_element_index = false;
38024#endif
38025 for (unsigned eg = 0; eg < nele; eg++)
38026 {
38027 // Get the "eg-th" element
38028 FiniteElement* lel_compare_pt = this->finite_element_pt(eg);
38029
38030 // Compare with the element on the boundary, if equal then
38031 // store the target area
38032 if (lel_pt == lel_compare_pt)
38033 {
38034 zeta_target_area_values[1] = target_area[eg];
38035#ifdef PARANOID
38036 found_global_element_index = true;
38037#endif
38038 break; // break the for (e < nele) global element
38039 } // if element_pt == element_compare_pt
38040 } // for nele (on complete mesh)
38041
38042#ifdef PARANOID
38043 if (!found_global_element_index)
38044 {
38045 std::ostringstream error_message;
38046 error_message << "The global index for the (" << ef
38047 << ")-th face element "
38048 << "on\nthe (" << bound
38049 << ")-th boundary was not found!!!";
38050 throw OomphLibError(error_message.str(),
38051 "RefineableTriangleMesh::update_polygon_"
38052 "using_elements_area()",
38053 OOMPH_EXCEPTION_LOCATION);
38054 }
38055#endif
38056
38057 // Add the target areas to the sorted set
38058 sorted_target_areas.insert(zeta_target_area_values);
38059
38060 break;
38061 }
38062
38063 } // if (!edge_done[edge])
38064 } // for (iiedge < nedges)
38065 } while (face_element_added &&
38066 (nsorted_face_elements < nnon_halo_face_element));
38067
38068 // -----------------------------------------------------------------
38069 // At this point we already have a sorted set of nodes and
38070 // can be used to peform the unrefinement and refinement procedures
38071 // -----------------------------------------------------------------
38072
38073 // Get the number of nodes on the list
38074 const unsigned nlocal_nodes = local_vertex_nodes.size();
38075 // Change representation to vector for easy of handling ...
38076 local_tmp_vector_vertex_node.resize(nlocal_nodes);
38077
38078 // Copy the vertices of the nodes
38079 unsigned counter = 0;
38080 std::set<Vector<double>>::iterator it_vertex;
38081 for (it_vertex = local_vertex_nodes.begin();
38082 it_vertex != local_vertex_nodes.end();
38083 it_vertex++)
38084 {
38085 local_tmp_vector_vertex_node[counter].resize(3);
38086 local_tmp_vector_vertex_node[counter][0] = (*it_vertex)[0];
38087 local_tmp_vector_vertex_node[counter][1] = (*it_vertex)[1];
38088 local_tmp_vector_vertex_node[counter][2] = (*it_vertex)[2];
38089 counter++;
38090 }
38091
38092 // ... same for the info. related with the target areas (turn
38093 // into vector)
38094 const unsigned ntarget_areas = sorted_target_areas.size();
38095 tmp_sorted_target_areas.resize(ntarget_areas);
38096 counter = 0;
38097 std::set<Vector<double>>::iterator it_area;
38098 for (it_area = sorted_target_areas.begin();
38099 it_area != sorted_target_areas.end();
38100 ++it_area)
38101 {
38102 tmp_sorted_target_areas[counter] = (*it_area)[1];
38103 ++counter;
38104 }
38105
38106#ifdef PARANOID
38107 if (nlocal_nodes > 0 && (ntarget_areas != nlocal_nodes - 1))
38108 {
38109 std::ostringstream error_message;
38110 error_message
38111 << "The boundary (" << bound << ") was split during the "
38112 << "distribution process.\n"
38113 << "The problem is in the association of the target areas with "
38114 "the\n"
38115 << "elements that gave rise to the vertex coordinates.\n"
38116 << "The number of local nodes (" << nlocal_nodes
38117 << "), on the 'sub-polyline', is not\n"
38118 << "according with the number of target "
38119 << "areas (" << ntarget_areas << ")\nfor that number of nodes.\n"
38120 << "The target areas number MUST be equal to the number of\n"
38121 << "local nodes minus one\n\n";
38122 throw OomphLibError(error_message.str(),
38123 OOMPH_CURRENT_FUNCTION,
38124 OOMPH_EXCEPTION_LOCATION);
38125 }
38126#endif
38127
38128 // -------------------------------------------------------------------
38129 // Update the vertices along the boundary using the target area
38130 // to define the distance among them
38131 // -------------------------------------------------------------------
38132
38133 // Tolerance below which the middle point can be deleted
38134 // (ratio of deflection to element length)
38135 double unrefinement_tolerance =
38136 polygon_pt->polyline_pt(p)->unrefinement_tolerance();
38137
38138 // Apply unrefinement
38139 bool unrefinement_applied =
38140 unrefine_boundary_constrained_by_target_area(
38141 bound,
38142 chunk,
38143 local_tmp_vector_vertex_node,
38144 unrefinement_tolerance,
38145 tmp_sorted_target_areas);
38146
38147 // Tolerance for refinement
38148 double refinement_tolerance =
38149 polygon_pt->polyline_pt(p)->refinement_tolerance();
38150
38151 // Apply refinement
38152 bool refinement_applied = refine_boundary_constrained_by_target_area(
38153 mesh_geom_obj_pt,
38154 local_tmp_vector_vertex_node,
38155 refinement_tolerance,
38156 tmp_sorted_target_areas);
38157
38158 // Clear the local containter to recover the nodes ordered using the
38159 // zeta value
38160 local_vertex_nodes.clear();
38161
38162 // At the end of each unrefinement/refinement step store the new nodes
38163 // on the set that will give rise to the vertices of the new polyline
38164 // representation
38165 unsigned nnew_nodes = local_tmp_vector_vertex_node.size();
38166 for (unsigned i = 0; i < nnew_nodes; i++)
38167 {
38168 vertex_coord[0] = local_tmp_vector_vertex_node[i][0];
38169 vertex_coord[1] = local_tmp_vector_vertex_node[i][1];
38170 vertex_coord[2] = local_tmp_vector_vertex_node[i][2];
38171 vertex_nodes.insert(vertex_coord); // Global container
38172 local_vertex_nodes.insert(vertex_coord);
38173 }
38174
38175 // Update the flag to indicate whether an unrefinement or
38176 // refinement was applied
38177 update_was_performed = (unrefinement_applied || refinement_applied);
38178
38179#ifdef OOMPH_HAS_MPI
38180 if (this->is_mesh_distributed())
38181 {
38182 // Add the set of vertices for the boundary, this will help to
38183 // detect if we need to deal with sub-boundaries
38184 sub_vertex_nodes.push_back(local_vertex_nodes);
38185 // Increase the counter for sub-boundaries
38186 nsub_boundaries++;
38187 }
38188#endif
38189
38190 } // while(nsorted_face_elements < nnon_halo_face_element)
38191
38192 // Now turn into vector for ease of handling...
38193 unsigned npoly_vertex = vertex_nodes.size();
38194 // This will store all the vertices whether the boundary was split
38195 // or not
38196 tmp_vector_vertex_node.resize(npoly_vertex);
38197 unsigned count = 0;
38198 for (std::set<Vector<double>>::iterator it = vertex_nodes.begin();
38199 it != vertex_nodes.end();
38200 ++it)
38201 {
38202 tmp_vector_vertex_node[count].resize(3);
38203 tmp_vector_vertex_node[count][0] = (*it)[0];
38204 tmp_vector_vertex_node[count][1] = (*it)[1];
38205 tmp_vector_vertex_node[count][2] = (*it)[2];
38206 ++count;
38207 }
38208
38209#ifdef OOMPH_HAS_MPI
38210 // --------- Stuff for the sub_boundaries ----- Begin section ---------
38211#ifdef PARANOID
38212 unsigned nsub_boundaries_set = sub_vertex_nodes.size();
38213 if (nsub_boundaries_set != nsub_boundaries)
38214 {
38215 std::ostringstream error_message;
38216 error_message
38217 << "The number of found sub-boundaries and the number of counted\n"
38218 << "sub-boundaries are different:\n"
38219 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
38220 << "Number of counted sub-boundaries: (" << nsub_boundaries << ")\n";
38221 throw OomphLibError(error_message.str(),
38222 OOMPH_CURRENT_FUNCTION,
38223 OOMPH_EXCEPTION_LOCATION);
38224 }
38225#endif
38226
38227 // Are there sub-boundaries (only appear in distributed meshes)
38228 if (this->is_mesh_distributed() && nsub_boundaries > 1)
38229 {
38230 // Mark the boundary as been splitted in the partition process
38231 this->Boundary_was_splitted[bound] = true;
38232 // Resize the vector to store the info. of sub-boundaries
38233 sub_tmp_vector_vertex_node.resize(nsub_boundaries);
38234 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
38235 {
38236 // Turn info. into vector for ease of handling...
38237 const unsigned nsubpoly_vertex = sub_vertex_nodes[isub].size();
38238 sub_tmp_vector_vertex_node[isub].resize(nsubpoly_vertex);
38239 unsigned subcount = 0;
38240 std::set<Vector<double>>::iterator subit;
38241 for (subit = sub_vertex_nodes[isub].begin();
38242 subit != sub_vertex_nodes[isub].end();
38243 ++subit)
38244 {
38245 sub_tmp_vector_vertex_node[isub][subcount].resize(3);
38246 sub_tmp_vector_vertex_node[isub][subcount][0] = (*subit)[0];
38247 sub_tmp_vector_vertex_node[isub][subcount][1] = (*subit)[1];
38248 sub_tmp_vector_vertex_node[isub][subcount][2] = (*subit)[2];
38249 ++subcount;
38250 }
38251 }
38252 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
38253 // --------- Stuff for the sub_boundaries ----- End section ------------
38254#endif // OOMPH_HAS_MPI
38255
38256 // For further processing the three-dimensional vector has to be
38257 // reduced to a two-dimensional vector
38258 unsigned n_vertex = tmp_vector_vertex_node.size();
38259
38260 // Resize the vector for vectices
38261 vector_vertex_node.resize(n_vertex);
38262 for (unsigned i = 0; i < n_vertex; i++)
38263 {
38264 vector_vertex_node[i].resize(2);
38265 vector_vertex_node[i][0] = tmp_vector_vertex_node[i][1];
38266 vector_vertex_node[i][1] = tmp_vector_vertex_node[i][2];
38267 }
38268
38269#ifdef OOMPH_HAS_MPI
38270 // --------- Stuff for the sub_boundaries ----- Begin section ----------
38271 // Verify if need to deal with sub_boundaries
38272 if (this->is_mesh_distributed() && nsub_boundaries > 1)
38273 {
38274 // For further processing the three-dimensional vector
38275 // has to be reduced to a two-dimensional vector
38276 // Resize the vector to store the info. of sub-boundaries
38277 sub_vector_vertex_node.resize(nsub_boundaries);
38278 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
38279 {
38280 const unsigned subn_vertex = sub_tmp_vector_vertex_node[isub].size();
38281 // Resize the vector for vectices
38282 sub_vector_vertex_node[isub].resize(subn_vertex);
38283 for (unsigned i = 0; i < subn_vertex; i++)
38284 {
38285 sub_vector_vertex_node[isub][i].resize(2);
38286 sub_vector_vertex_node[isub][i][0] =
38287 sub_tmp_vector_vertex_node[isub][i][1];
38288 sub_vector_vertex_node[isub][i][1] =
38289 sub_tmp_vector_vertex_node[isub][i][2];
38290 }
38291 }
38292 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
38293
38294 // We already have the info. for the sub-boundaries (if necessary)
38295 // and then we can create the sub-boundaries representations to
38296 // ease the generation of the mesh by Triangle
38297
38298 // --------- Stuff for the sub_boundaries ----- End section ------------
38299#endif // OOMPH_HAS_MPI
38300
38301 // --------------------------------------------------------------------
38302 // Check for contiguousness
38303 // --------------------------------------------------------------------
38304#ifdef OOMPH_HAS_MPI
38305 // Only perform this checking if the mesh is not distributed. When
38306 // the mesh is distributed the polylines continuity is addressed
38307 // by the sort_polylines_helper() method
38308 if (!this->is_mesh_distributed())
38309#endif
38310 {
38311 if (p > 0)
38312 {
38313 // Final end point of previous line
38314 Vector<double> final_vertex_of_previous_segment;
38315 unsigned n_prev_vertex =
38316 polygon_pt->curve_section_pt(p - 1)->nvertex();
38317 final_vertex_of_previous_segment =
38318 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(n_prev_vertex -
38319 1);
38320
38321 unsigned prev_seg_boundary_id =
38322 polygon_pt->curve_section_pt(p - 1)->boundary_id();
38323
38324 // Find the error between the final vertex of the previous
38325 // line and the first vertex of the current line
38326 double error = 0.0;
38327 for (unsigned i = 0; i < 2; i++)
38328 {
38329 const double dist = final_vertex_of_previous_segment[i] -
38330 (*vector_vertex_node.begin())[i];
38331 error += dist * dist;
38332 }
38333 error = sqrt(error);
38334
38335 // If the error is bigger than the tolerance then
38336 // we probably need to reverse, but better check
38338 {
38339 // Find the error between the final vertex of the previous
38340 // line and the last vertex of the current line
38341 double rev_error = 0.0;
38342 for (unsigned i = 0; i < 2; i++)
38343 {
38344 const double dist = final_vertex_of_previous_segment[i] -
38345 (*--vector_vertex_node.end())[i];
38346 rev_error += dist * dist;
38347 }
38348 rev_error = sqrt(rev_error);
38349
38350 if (rev_error >
38352 {
38353 // It could be possible that the first segment be reversed
38354 // and we did not notice it because this check does not
38355 // apply for the first segment. We can verify if the first
38356 // segment is reversed by using the vertex number 1
38357 if (p == 1)
38358 {
38359 // Initial end point of previous line
38360 Vector<double> initial_vertex_of_previous_segment;
38361
38362 initial_vertex_of_previous_segment =
38363 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(0);
38364
38365 unsigned prev_seg_boundary_id =
38366 polygon_pt->curve_section_pt(p - 1)->boundary_id();
38367
38368 // Find the error between the initial vertex of the previous
38369 // line and the first vertex of the current line
38370 double error = 0.0;
38371 for (unsigned i = 0; i < 2; i++)
38372 {
38373 const double dist = initial_vertex_of_previous_segment[i] -
38374 (*vector_vertex_node.begin())[i];
38375 error += dist * dist;
38376 }
38377 error = sqrt(error); // Reversed only the previous one
38378
38379 // If the error is bigger than the tolerance then
38380 // we probably need to reverse, but better check
38381 if (error >
38383 {
38384 // Find the error between the final vertex of the previous
38385 // line and the last vertex of the current line
38386 double rev_error = 0.0;
38387 for (unsigned i = 0; i < 2; i++)
38388 {
38389 const double dist = initial_vertex_of_previous_segment[i] -
38390 (*--vector_vertex_node.end())[i];
38391 rev_error += dist * dist;
38392 }
38393 rev_error =
38394 sqrt(rev_error); // Reversed both the current one and
38395 // the previous one
38396
38397 if (rev_error >
38399 {
38400 std::ostringstream error_stream;
38401 error_stream
38402 << "The distance between the first node of the current\n"
38403 << "line segment (boundary " << bound
38404 << ") and either end of "
38405 << "the previous line segment\n"
38406 << "(boundary " << prev_seg_boundary_id
38407 << ") is bigger than "
38408 << "the desired tolerance "
38410 << ".\n"
38411 << "This suggests that the polylines defining the "
38412 << "polygonal\n"
38413 << "representation are not properly ordered.\n"
38414 << "Fail on last vertex of polyline: ("
38415 << prev_seg_boundary_id << ") and\n"
38416 << "first vertex of polyline (" << bound << ").\n"
38417 << "This should have failed when first trying to "
38418 << "construct the\npolygon.\n";
38419 throw OomphLibError(error_stream.str(),
38420 OOMPH_CURRENT_FUNCTION,
38421 OOMPH_EXCEPTION_LOCATION);
38422 }
38423 else
38424 {
38425 // Reverse both
38426 // Reverse the current vector to line up with the
38427 // previous one
38428 std::reverse(vector_vertex_node.begin(),
38429 vector_vertex_node.end());
38430
38431 polygon_pt->polyline_pt(p - 1)->reverse();
38432 }
38433 }
38434 else
38435 {
38436 // Reverse the previous one
38437 polygon_pt->polyline_pt(p - 1)->reverse();
38438 }
38439
38440 } // if p == 1
38441 else
38442 {
38443 std::ostringstream error_stream;
38444 error_stream
38445 << "The distance between the first node of the current\n"
38446 << "line segment (boundary " << bound
38447 << ") and either end of "
38448 << "the previous line segment\n"
38449 << "(boundary " << prev_seg_boundary_id
38450 << ") is bigger than the "
38451 << "desired tolerance "
38453 << ".\n"
38454 << "This suggests that the polylines defining the polygonal\n"
38455 << "representation are not properly ordered.\n"
38456 << "Fail on last vertex of polyline: ("
38457 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
38458 << bound << ").\n"
38459 << "This should have failed when first trying to construct"
38460 << " the polygon.\n";
38461 throw OomphLibError(error_stream.str(),
38462 OOMPH_CURRENT_FUNCTION,
38463 OOMPH_EXCEPTION_LOCATION);
38464 }
38465 }
38466 else
38467 {
38468 // Reverse the current vector to line up with the previous one
38469 std::reverse(vector_vertex_node.begin(),
38470 vector_vertex_node.end());
38471 }
38472 } // error
38473
38474 } // if ( p > 0 )
38475
38476 } // if (!this->is_mesh_distributed())
38477
38478 // --------------------------------------------------------------------
38479 // Update the polylines representation
38480 // --------------------------------------------------------------------
38481
38482 // Always update the polylines representation, in a distributed
38483 // mesh it is necessary to update the polyline representation since
38484 // it may no longer have vertices (the boundary may not be part of
38485 // the domain in the current processor)
38486
38487 // The new nunber of vertices
38488 n_vertex = vector_vertex_node.size();
38489
38490 // Now update the polyline according to the new vertices
38491 TriangleMeshPolyLine* tmp_polyline_pt =
38492 new TriangleMeshPolyLine(vector_vertex_node, bound);
38493
38494 // Create a temporal "curve section" version of the recently
38495 // created polyline
38496 TriangleMeshCurveSection* tmp_curve_section_pt = tmp_polyline_pt;
38497
38498 // Tolerance below which the middle point can be deleted (ratio of
38499 // deflection to element length)
38500 double unrefinement_tolerance =
38501 polygon_pt->polyline_pt(p)->unrefinement_tolerance();
38502
38503 // Tolerance to add points
38504 double refinement_tolerance =
38505 polygon_pt->polyline_pt(p)->refinement_tolerance();
38506
38507 // Establish refinement and unrefinement tolerance
38508 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
38509 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
38510
38511 // Establish the maximum length constraint
38512 double maximum_length = polygon_pt->polyline_pt(p)->maximum_length();
38513 tmp_polyline_pt->set_maximum_length(maximum_length);
38514
38515#ifdef OOMPH_HAS_MPI
38516 // If the mesh is distributed check that the polyline still has
38517 // vertices
38518 if (this->is_mesh_distributed())
38519 {
38520 if (n_vertex >= 2)
38521 {
38522 // Pass the connection information from the old polyline to the
38523 // new one
38524 this->copy_connection_information(polygon_pt->polyline_pt(p),
38525 tmp_curve_section_pt);
38526 } // if (n_vertex >= 2)
38527 } // if (this->is_mesh_distributed())
38528 else
38529#endif
38530 {
38531 // Pass the connection information from the old polyline to the
38532 // new one
38533 this->copy_connection_information(polygon_pt->polyline_pt(p),
38534 tmp_curve_section_pt);
38535 }
38536
38537 // Now update the polyline according to the new vertices but first
38538 // check if the object is allowed to delete the representation or
38539 // if it should be done by other object
38540 bool delete_it_on_destructor = false;
38541
38542 std::set<TriangleMeshCurveSection*>::iterator it =
38543 this->Free_curve_section_pt.find(polygon_pt->curve_section_pt(p));
38544
38545 if (it != this->Free_curve_section_pt.end())
38546 {
38547 this->Free_curve_section_pt.erase(it);
38548 delete polygon_pt->curve_section_pt(p);
38549 delete_it_on_destructor = true;
38550 }
38551
38552 // -------------------------------------------------------
38553 // Copying the new representation
38554 polygon_pt->curve_section_pt(p) = tmp_polyline_pt;
38555
38556 // Update the Boundary - Polyline map
38557 this->Boundary_curve_section_pt[bound] = polygon_pt->curve_section_pt(p);
38558
38559 if (delete_it_on_destructor)
38560 {
38561 this->Free_curve_section_pt.insert(polygon_pt->curve_section_pt(p));
38562 }
38563
38564#ifdef OOMPH_HAS_MPI
38565 // --------- Stuff for the sub_boundaries ----- Begin section --------
38566 // Verify if need to deal with sub_boundaries
38567 if (this->is_mesh_distributed() && nsub_boundaries > 1)
38568 {
38569 // Create temporary representations for the boundaries, only to
38570 // create the mesh when calling Triangle
38571
38572 // Clear all previous stored data
38573 this->Boundary_subpolylines[bound].clear();
38574
38575 // Create storage for the sub-boundaries
38576 this->Boundary_subpolylines[bound].resize(nsub_boundaries);
38577 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
38578 {
38579 // Update the polyline according to the sub set of vertices,
38580 TriangleMeshPolyLine* sub_tmp_polyline_pt =
38581 new TriangleMeshPolyLine(sub_vector_vertex_node[isub], bound, isub);
38582
38583 // Add the sub-polyline to the container to represent the
38584 // boundary in parts
38585 this->Boundary_subpolylines[bound][isub] = sub_tmp_polyline_pt;
38586
38587 // No need to send the unrefinement/refinement and maximum
38588 // length constraints since these are only temporary
38589 // representations. These polylines can be deleted once the new
38590 // polygons that represent the distributed domain have been
38591 // created
38592
38593 } // for (isub < nsub_boundaries)
38594
38595 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
38596 // --------- Stuff for the sub_boundaries ----- End section ---------
38597#endif // OOMPH_HAS_MPI
38598
38599 // Delete the allocated memory for the geometric object that
38600 // represents the boundary
38601 delete mesh_geom_obj_pt;
38602
38603 } // for (p < n_polyline)
38604
38605 // Cleanup the face mesh
38606 for (unsigned p = 0; p < n_polyline; p++)
38607 {
38608 face_mesh_pt[p]->flush_node_storage();
38609 delete face_mesh_pt[p];
38610 }
38611
38612 return update_was_performed;
38613 }
38614
38615 //======================================================================
38616 /// Updates the open curve but using the elements area instead
38617 /// of the default refinement and unrefinement methods
38618 //======================================================================
38619 template<class ELEMENT>
38621 TriangleMeshOpenCurve*& open_curve_pt, const Vector<double>& target_area)
38622 {
38623 // Verify if there was a change on the open curve representation
38624 unsigned update_was_performed = false;
38625
38626 const unsigned nele = this->nelement();
38627
38628 // - Get the vertices along the boundaries and for each element identify
38629 // its associated target error.
38630 // - Get face mesh representation of each polyline.
38631 // - Get the vertices with the help of face elements.
38632 // - Find the global index in the mesh of the face element
38633 // and use it to get its associated target area.
38634
38635 // Get the face mesh representation
38636 Vector<Mesh*> face_mesh_pt;
38637 get_face_mesh_representation(open_curve_pt, face_mesh_pt);
38638
38639 // Create vertices of the polylines by using the vertices of the
38640 // FaceElements
38641 Vector<double> vertex_coord(3); // zeta,x,y
38642 Vector<double> bound_left(1);
38643 Vector<double> bound_right(1);
38644
38645 const unsigned ncurve_section = open_curve_pt->ncurve_section();
38646
38647 // Go for each curve section
38648 for (unsigned cs = 0; cs < ncurve_section; cs++)
38649 {
38650 // Get the MeshAsGeomObject representation just once per polyline,
38651 // this object is only used by the
38652 // refine_boundary_constrained_by_target_area() method. We get it
38653 // here to ensure that all processors (in a distributed context)
38654 // get this representation just once, and because an AllToAll MPI
38655 // communication is used in this calling
38656 MeshAsGeomObject* mesh_geom_obj_pt =
38657 new MeshAsGeomObject(face_mesh_pt[cs]);
38658
38659 // Get the boundary id
38660 const unsigned bound = open_curve_pt->curve_section_pt(cs)->boundary_id();
38661
38662 // Get the chunk number
38663 const unsigned chunk =
38664 open_curve_pt->curve_section_pt(cs)->boundary_chunk();
38665
38666 /// Use a vector of vector for vertices and target areas to deal
38667 /// with the cases when the boundaries are split by the
38668 /// distribution process. Internal boundaries may be completely or
38669 /// partially overlapped by shared boundaries
38670
38671 // Loop over the face elements and add their vertices (they are
38672 // automatically sorted because of the set)
38673 const unsigned nface_element = face_mesh_pt[cs]->nelement();
38674
38675 // Store the non halo elements and the element at the other side of
38676 // the boundary (whatever it be halo or not), the first will be the
38677 // ones from which we will get the vertices (in even position)
38678 Vector<FiniteElement*> non_halo_doubled_face_element_pt;
38679
38680 // Map to store the index of the face element on a boundary
38681 std::map<FiniteElement*, unsigned> face_element_index_on_boundary;
38682
38683 // Map to know the already sorted face elements
38684 std::map<FiniteElement*, bool> face_element_done;
38685
38686 for (unsigned ef = 0; ef < nface_element; ++ef)
38687 {
38688 FiniteElement* ele_face_pt = face_mesh_pt[cs]->finite_element_pt(ef);
38689
38690 // Skip the halo elements (not used as base elements, only
38691 // include those elements whose element at the other side of the
38692 // boundary is non halo)
38693#ifdef OOMPH_HAS_MPI
38694 if (this->is_mesh_distributed())
38695 {
38696 // Only work with non-halo elements
38697 if (ele_face_pt->is_halo())
38698 {
38699 continue;
38700 }
38701 }
38702#endif
38703
38704 // Check if not already done
38705 if (!face_element_done[ele_face_pt])
38706 {
38707 // Add the element and look for the element at the other side
38708 // of the boundary to add it immediately after the new added
38709 // element
38710 non_halo_doubled_face_element_pt.push_back(ele_face_pt);
38711 // Create the map of the face element with the index
38712 face_element_index_on_boundary[ele_face_pt] = ef;
38713 // Mark the current element as done
38714 face_element_done[ele_face_pt] = true;
38715 // Get the number of nodes
38716 const unsigned nnodes = ele_face_pt->nnode();
38717 // Get the left and right node to look for the elements at the
38718 // other side of the boundary
38719 Node* left_node_pt = ele_face_pt->node_pt(0);
38720 Node* right_node_pt = ele_face_pt->node_pt(nnodes - 1);
38721#ifdef PARANOID
38722 // Flag to know if the element at the other side of the
38723 // boundary was found
38724 bool found_other_side_face_ele = false;
38725#endif
38726 for (unsigned iface = 0; iface < nface_element; iface++)
38727 {
38728 // Get the candidate face element
38729 FiniteElement* cele_face_pt =
38730 face_mesh_pt[cs]->finite_element_pt(iface);
38731 // Check if not already done
38732 if (!face_element_done[cele_face_pt])
38733 {
38734 Node* cleft_node_pt = cele_face_pt->node_pt(0);
38735 Node* cright_node_pt = cele_face_pt->node_pt(nnodes - 1);
38736 // Check if the nodes are the same
38737 if ((left_node_pt == cleft_node_pt &&
38738 right_node_pt == cright_node_pt) ||
38739 (left_node_pt == cright_node_pt &&
38740 right_node_pt == cleft_node_pt))
38741 {
38742 // Add the element to the storage
38743 non_halo_doubled_face_element_pt.push_back(cele_face_pt);
38744 // ... and mark the element as done
38745 face_element_done[cele_face_pt] = true;
38746 // Create the map of the face element with the index
38747 face_element_index_on_boundary[cele_face_pt] = iface;
38748#ifdef PARANOID
38749 // Set the flag of found other side face element
38750 found_other_side_face_ele = true;
38751#endif
38752 break;
38753 }
38754 }
38755 } // (iface < nface_element)
38756
38757#ifdef PARANOID
38758 if (!found_other_side_face_ele)
38759 {
38760 std::ostringstream error_message;
38761 error_message
38762 << "The face element at the other side of the boundary (" << bound
38763 << ") was not found!!\n"
38764 << "These are the nodes of the face element:\n"
38765 << "(" << left_node_pt->x(0) << ", " << left_node_pt->x(1) << ") "
38766 << "and (" << right_node_pt->x(0) << "," << right_node_pt->x(1)
38767 << ")\n\n";
38768 throw OomphLibError(
38769 error_message.str(),
38770 "RefineableTriangleMesh::update_open_curve_using_elements_area()",
38771 OOMPH_EXCEPTION_LOCATION);
38772 }
38773#endif
38774 } // if (!face_ele_done[ele_face_pt])
38775
38776 } // (ef < nface_element)
38777
38778 // Clear the map of the already done face elements
38779 // This will be used to help sorting the face elements
38780 face_element_done.clear();
38781
38782 // Set of coordinates that are on the boundary
38783 // The entries are sorted on first entry in vector which stores
38784 // the boundary coordinate so the vertices come out in order!
38785 std::set<Vector<double>> vertex_nodes;
38786
38787 // Vector to store the vertices, transfer the sorted vertices from the
38788 // set to this vector, --- including the z-value ---
38789 Vector<Vector<double>> tmp_vector_vertex_node;
38790
38791 // Vector to store the coordinates of the polylines, same as the
38792 // tmp_vector_vertex_node vector (after adding more nodes) but
38793 // --- without the z-value ---, used to re-generate the polylines
38794 Vector<Vector<double>> vector_vertex_node;
38795
38796#ifdef OOMPH_HAS_MPI
38797 // Indicates if the set of vertices give rise to a internal
38798 // boundary that will be used as shared boundary or as normal
38799 // internal boundary -- Only used to deal with internal boundaries
38800 // in a distributed scheme
38801 std::vector<bool> internal_to_shared_boundary;
38802
38803 // --------- Stuff to deal with splitted boundaries ---------- Begin -----
38804 // Set of coordinates that are on the boundary (splitted boundary version)
38805 // The first vector is used to allocate the points for each sub-boundary
38806 // Set entries are ordered on first entry in vector which stores
38807 // the boundary coordinate so the vertices come out in order!
38808 Vector<std::set<Vector<double>>> sub_vertex_nodes;
38809
38810 // Vector to store the vertices, transfer the sorted vertices from the
38811 // set (sub_vertex_nodes) to this vector, --- including the z-value ---
38812 Vector<Vector<Vector<double>>> sub_tmp_vector_vertex_node;
38813
38814 // Vector to store the coordinates of the polylines that will represent
38815 // the splitted boundary. Used to pass the info. from sub_vertex_nodes
38816 // but --- without the z-value ---, used to generate the sub-polylines
38817 Vector<Vector<Vector<double>>> sub_vector_vertex_node;
38818
38819 // --------- Stuff to deal with splitted boundaries ----------- End ------
38820
38821#endif // #ifdef OOMPH_HAS_MPI
38822
38823 // Sort the face element, those that have both elements (one at
38824 // each side of the boundary) marked as nonhalo, and those with one
38825 // nonhalo an the other as halo
38826
38827 // Number of done face elements
38828 unsigned nsorted_face_elements = 0;
38829
38830#ifdef OOMPH_HAS_MPI
38831 // Counter for sub_boundaries
38832 unsigned nsub_boundaries = 0;
38833#endif // #ifdef OOMPH_HAS_MPI
38834
38835 // Total number of non halo double face element
38836 const unsigned nnon_halo_doubled_face_ele =
38837 non_halo_doubled_face_element_pt.size();
38838
38839 // Continue until all the face elements have been sorted
38840 // This while is to deal with the cases of splitted boundaries
38841 while (nsorted_face_elements < nnon_halo_doubled_face_ele)
38842 {
38843 // Get and initial face element
38844 FiniteElement* ele_face_pt = 0;
38845 FiniteElement* repeated_ele_face_pt = 0;
38846#ifdef PARANOID
38847 bool found_initial_face_element = false;
38848#endif
38849
38850 // Flag to know if we are working with a face element which the
38851 // face element at the other side of the boundary is also non
38852 // halo
38853 bool both_root_face_elements_are_nonhalo = false;
38854
38855 unsigned iface = 0;
38856 for (iface = 0; iface < nnon_halo_doubled_face_ele; iface += 2)
38857 {
38858 ele_face_pt = non_halo_doubled_face_element_pt[iface];
38859 // If not done then take it as initial face element
38860 if (!face_element_done[ele_face_pt])
38861 {
38862 // Mark it as done
38863 face_element_done[ele_face_pt] = true;
38864 // Get the other side boundary face element
38865 repeated_ele_face_pt = non_halo_doubled_face_element_pt[iface + 1];
38866 // ... also mark as done the repeated face element
38867 face_element_done[repeated_ele_face_pt] = true;
38868
38869#ifdef OOMPH_HAS_MPI
38870 if (!repeated_ele_face_pt->is_halo())
38871 {
38872 both_root_face_elements_are_nonhalo = true;
38873 }
38874#endif // #ifdef OOMPH_HAS_MPI
38875
38876 // Plus two because internal boundaries have
38877 // two face elements per each edge
38878 nsorted_face_elements += 2;
38879 iface += 2;
38880#ifdef PARANOID
38881 // And set the flag to true
38882 found_initial_face_element = true;
38883#endif
38884 break;
38885 }
38886 }
38887
38888#ifdef PARANOID
38889 if (!found_initial_face_element)
38890 {
38891 std::ostringstream error_message;
38892 error_message << "Could not find an initial face element for the "
38893 "current segment\n";
38894 throw OomphLibError(error_message.str(),
38895 OOMPH_CURRENT_FUNCTION,
38896 OOMPH_EXCEPTION_LOCATION);
38897 }
38898#endif
38899
38900 // Local set of coordinates that are on the boundary Set entries
38901 // are ordered on first entry in vector which stores the boundary
38902 // coordinate so the vertices come out in order
38903 std::set<Vector<double>> local_vertex_nodes;
38904
38905 // Vector to store the vertices, transfer the sorted vertices from the
38906 // set (local) to this vector (local), --- including the z-value ---
38907 Vector<Vector<double>> local_tmp_vector_vertex_node;
38908
38909 // Vector to store the target areas, uses the same approach as the
38910 // set for the local_vertex_nodes, ordered on first entry
38911 std::set<Vector<double>> sorted_target_areas;
38912
38913 // Vector to store the target areas, used to transfer the sorted target
38914 // areas from "sorted_target_areas" set
38915 Vector<double> tmp_sorted_target_areas;
38916
38917 // ------------------------------------------------------------------
38918 // Add the vertices of the initial face element to the set of local
38919 // sorted vertices
38920 // ------------------------------------------------------------------
38921 const unsigned nnode = ele_face_pt->nnode();
38922 // Add the left-hand node to the set:
38923 // Boundary coordinate
38924 ele_face_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
38925 vertex_coord[0] = bound_left[0];
38926
38927 // Actual coordinates
38928 for (unsigned i = 0; i < 2; i++)
38929 {
38930 vertex_coord[i + 1] = ele_face_pt->node_pt(0)->x(i);
38931 }
38932 local_vertex_nodes.insert(vertex_coord);
38933
38934 // Add the right-hand node to the set:
38935 // Boundary coordinate
38936 ele_face_pt->node_pt(nnode - 1)->get_coordinates_on_boundary(
38937 bound, bound_right);
38938 vertex_coord[0] = bound_right[0];
38939
38940 // Actual coordinates
38941 for (unsigned i = 0; i < 2; i++)
38942 {
38943 vertex_coord[i + 1] = ele_face_pt->node_pt(nnode - 1)->x(i);
38944 }
38945 local_vertex_nodes.insert(vertex_coord);
38946
38947 // The initial and final node on the set
38948 Node* first_node_pt = ele_face_pt->node_pt(0);
38949 Node* last_node_pt = ele_face_pt->node_pt(nnode - 1);
38950
38951 // -----------------------------------------------------
38952 // Find the global index in the mesh of the face element
38953 // and use it to get its associated target area
38954 // -----------------------------------------------------
38955 // Container to store the zeta value (used as index) and
38956 // the associated target area of the element
38957 Vector<double> zeta_target_area_values(2);
38958
38959 // Use the minimum zeta value to sort the target areas
38960 // along the boundary
38961 zeta_target_area_values[0] = std::min(bound_left[0], bound_right[0]);
38962
38963 // Get the index of the face element on the current boundary
38964 const unsigned ef = face_element_index_on_boundary[ele_face_pt];
38965 // Get the "ef"-th element on the boundary
38966 FiniteElement* el_pt = this->boundary_element_pt(bound, ef);
38967 double target_area_face_element = 0.0;
38968
38969#ifdef PARANOID
38970 bool found_global_element_index = false;
38971#endif
38972 for (unsigned eg = 0; eg < nele; eg++)
38973 {
38974 // Get the "eg-th" element
38975 FiniteElement* el_compare_pt = this->finite_element_pt(eg);
38976
38977 // Compare with the element on the boundary, if equal then
38978 // store the target area
38979 if (el_pt == el_compare_pt)
38980 {
38981 target_area_face_element = target_area[eg];
38982#ifdef PARANOID
38983 found_global_element_index = true;
38984#endif
38985 break; // break the for (eg < nele) global element
38986 } // if el_pt == el_compare_pt
38987 } // for nele (on complete mesh)
38988
38989#ifdef PARANOID
38990 if (!found_global_element_index)
38991 {
38992 std::ostringstream error_message;
38993 error_message << "The global index for the (" << ef
38994 << ")-th face element "
38995 << "on\nthe (" << bound
38996 << ")-th boundary was not found!!!";
38997 throw OomphLibError(error_message.str(),
38998 OOMPH_CURRENT_FUNCTION,
38999 OOMPH_EXCEPTION_LOCATION);
39000 }
39001#endif
39002
39003 // Get the index of the repeated face element on the current boundary
39004 const unsigned ref =
39005 face_element_index_on_boundary[repeated_ele_face_pt];
39006 FiniteElement* rel_pt = this->boundary_element_pt(bound, ref);
39007 double target_area_repeated_face_element = 0.0;
39008
39009#ifdef PARANOID
39010 bool found_global_repeated_element_index = false;
39011#endif
39012 for (unsigned eg = 0; eg < nele; eg++)
39013 {
39014 // Get the "eg-th" element
39015 FiniteElement* el_compare_pt = this->finite_element_pt(eg);
39016
39017 // Compare with the element on the boundary, if equal then
39018 // store the target area
39019 if (rel_pt == el_compare_pt)
39020 {
39021 target_area_repeated_face_element = target_area[eg];
39022#ifdef PARANOID
39023 found_global_repeated_element_index = true;
39024#endif
39025 break; // break the for (eg < nele) global element
39026 } // if rel_pt == el_compare_pt
39027 } // for nele (on complete mesh)
39028
39029#ifdef PARANOID
39030 if (!found_global_repeated_element_index)
39031 {
39032 std::ostringstream error_message;
39033 error_message << "The global index for the (" << ref
39034 << ")-th face element "
39035 << "on\nthe (" << bound
39036 << ")-th boundary was not found (repeated "
39037 << "face element)!!!";
39038 throw OomphLibError(error_message.str(),
39039 OOMPH_CURRENT_FUNCTION,
39040 OOMPH_EXCEPTION_LOCATION);
39041 }
39042#endif
39043
39044 // Choose the minimum target area from both elements, one at each side
39045 // of the edge on the boundary
39046 zeta_target_area_values[1] =
39047 std::min(target_area_face_element, target_area_repeated_face_element);
39048
39049 // Add the target areas to the sorted set
39050 sorted_target_areas.insert(zeta_target_area_values);
39051 // ------------------------------------------------------------------
39052
39053 // Continue iterating if a new face element has been added to the
39054 // list
39055 bool face_element_added = false;
39056
39057 // While a new face element has been added to the set of sorted
39058 // face elements then re-iterate
39059 do
39060 {
39061 // Start from the next face elements since we have already
39062 // added the previous one as the initial face element (any
39063 // previous face element had to be added on previous
39064 // iterations)
39065 for (unsigned iiface = iface; iiface < nnon_halo_doubled_face_ele;
39066 iiface += 2)
39067 {
39068 face_element_added = false;
39069 ele_face_pt = non_halo_doubled_face_element_pt[iiface];
39070
39071 // Check that the face element with which we are working has
39072 // the same conditions as the root face element (both faces
39073 // are nonhalo or one face is halo and the other nonhalo)
39074
39075 // Get the face element at the other side of the boundary
39076 repeated_ele_face_pt = non_halo_doubled_face_element_pt[iiface + 1];
39077 bool both_face_elements_are_nonhalo = false;
39078
39079#ifdef OOMPH_HAS_MPI
39080 if (!repeated_ele_face_pt->is_halo())
39081 {
39082 both_face_elements_are_nonhalo = true;
39083 }
39084#endif // #ifdef OOMPH_HAS_MPI
39085
39086 if (!face_element_done[ele_face_pt] &&
39087 (both_face_elements_are_nonhalo ==
39088 both_root_face_elements_are_nonhalo))
39089 {
39090 // Get each individual node to check if they are contiguous
39091 const unsigned nlnode = ele_face_pt->nnode();
39092 Node* left_node_pt = ele_face_pt->node_pt(0);
39093 Node* right_node_pt = ele_face_pt->node_pt(nlnode - 1);
39094
39095 if (left_node_pt == first_node_pt)
39096 {
39097 first_node_pt = right_node_pt;
39098 face_element_added = true;
39099 }
39100 else if (left_node_pt == last_node_pt)
39101 {
39102 last_node_pt = right_node_pt;
39103 face_element_added = true;
39104 }
39105 else if (right_node_pt == first_node_pt)
39106 {
39107 first_node_pt = left_node_pt;
39108 face_element_added = true;
39109 }
39110 else if (right_node_pt == last_node_pt)
39111 {
39112 last_node_pt = left_node_pt;
39113 face_element_added = true;
39114 }
39115
39116 if (face_element_added)
39117 {
39118 // Add the left-hand node to the set:
39119 // Boundary coordinate
39120 left_node_pt->get_coordinates_on_boundary(bound, bound_left);
39121 vertex_coord[0] = bound_left[0];
39122
39123 // Actual coordinates
39124 for (unsigned i = 0; i < 2; i++)
39125 {
39126 vertex_coord[i + 1] = left_node_pt->x(i);
39127 }
39128 local_vertex_nodes.insert(vertex_coord);
39129
39130 // Add the right-hand nodes to the set:
39131 // Boundary coordinate
39132 right_node_pt->get_coordinates_on_boundary(bound, bound_right);
39133 vertex_coord[0] = bound_right[0];
39134
39135 // Actual coordinates
39136 for (unsigned i = 0; i < 2; i++)
39137 {
39138 vertex_coord[i + 1] = right_node_pt->x(i);
39139 }
39140 local_vertex_nodes.insert(vertex_coord);
39141
39142 // Mark as done only if one of its nodes has been
39143 // added to the list
39144 face_element_done[ele_face_pt] = true;
39145 // .. also mark as done the face element at the othe side of
39146 // the boundary
39147 repeated_ele_face_pt =
39148 non_halo_doubled_face_element_pt[iiface + 1];
39149 face_element_done[repeated_ele_face_pt] = true;
39150 // ... and increase the number of sorted face elements
39151 nsorted_face_elements += 2;
39152
39153 // -----------------------------------------------------
39154 // Find the global index in the mesh of the face element
39155 // and use it to get its associated target area
39156 // -----------------------------------------------------
39157 // Use the minimum zeta value to sort the target areas
39158 // along the boundary
39159 zeta_target_area_values[0] =
39160 std::min(bound_left[0], bound_right[0]);
39161
39162 // Get the "ef"-th element on the boundary
39163 const unsigned lef =
39164 face_element_index_on_boundary[ele_face_pt];
39165 FiniteElement* lel_pt = this->boundary_element_pt(bound, lef);
39166
39167#ifdef PARANOID
39168 found_global_element_index = false;
39169#endif
39170 for (unsigned eg = 0; eg < nele; eg++)
39171 {
39172 // Get the "eg-th" element
39173 FiniteElement* lel_compare_pt = this->finite_element_pt(eg);
39174
39175 // Compare with the element on the boundary, if equal then
39176 // store the target area
39177 if (lel_pt == lel_compare_pt)
39178 {
39179 target_area_face_element = target_area[eg];
39180#ifdef PARANOID
39181 found_global_element_index = true;
39182#endif
39183 break; // break the for (eg < nele) global element
39184 } // if lel_pt == lel_compare_pt
39185 } // for nele (on complete mesh)
39186
39187#ifdef PARANOID
39188 if (!found_global_element_index)
39189 {
39190 std::ostringstream error_message;
39191 error_message << "The global index for the (" << lef
39192 << ")-th face element "
39193 << "on\nthe (" << bound
39194 << ")-th boundary was not found!!!";
39195 throw OomphLibError(error_message.str(),
39196 OOMPH_CURRENT_FUNCTION,
39197 OOMPH_EXCEPTION_LOCATION);
39198 }
39199#endif
39200
39201 // Get the index of the repeated face element on the boundary
39202 const unsigned rlef =
39203 face_element_index_on_boundary[repeated_ele_face_pt];
39204 FiniteElement* rlel_pt = this->boundary_element_pt(bound, rlef);
39205
39206#ifdef PARANOID
39207 found_global_repeated_element_index = false;
39208#endif
39209 for (unsigned eg = 0; eg < nele; eg++)
39210 {
39211 // Get the "eg-th" element
39212 FiniteElement* lel_compare_pt = this->finite_element_pt(eg);
39213
39214 // Compare with the element on the boundary, if equal then
39215 // store the target area
39216 if (rlel_pt == lel_compare_pt)
39217 {
39218 target_area_repeated_face_element = target_area[eg];
39219#ifdef PARANOID
39220 found_global_repeated_element_index = true;
39221#endif
39222 break; // break the for (eg < nele) global element
39223 } // if rlel_pt == el_compare_pt
39224 } // for nele (on complete mesh)
39225
39226#ifdef PARANOID
39227 if (!found_global_repeated_element_index)
39228 {
39229 std::ostringstream error_message;
39230 error_message << "The global index for the (" << rlef
39231 << ")-th face element "
39232 << "on\nthe (" << bound
39233 << ")-th boundary was not found "
39234 << "(repeated face element)!!!";
39235 throw OomphLibError(error_message.str(),
39236 OOMPH_CURRENT_FUNCTION,
39237 OOMPH_EXCEPTION_LOCATION);
39238 }
39239#endif
39240
39241 // Choose the minimum target area from both elements, one
39242 // at each side of the edge on the boundary
39243 zeta_target_area_values[1] = std::min(
39244 target_area_face_element, target_area_repeated_face_element);
39245
39246 // Add the target areas to the sorted set
39247 sorted_target_areas.insert(zeta_target_area_values);
39248
39249 break;
39250 }
39251
39252 } // if (!face_element_done[[ele_face_pt])
39253 } // for (iiface<nnon_halo_doubled_face_ele)
39254 } while (face_element_added &&
39255 (nsorted_face_elements < nnon_halo_doubled_face_ele));
39256
39257 // -------------------------------------------------------------
39258 // At this point we already have a sorted set of nodes and can
39259 // be used to peform the unrefinement and refinement procedures
39260 // -------------------------------------------------------------
39261
39262 // Get the number of nodes on the list
39263 const unsigned nlocal_nodes = local_vertex_nodes.size();
39264 // Change representation to vector for easy of handling ...
39265 local_tmp_vector_vertex_node.resize(nlocal_nodes);
39266
39267 // Copy the vertices of the nodes
39268 unsigned counter = 0;
39269 std::set<Vector<double>>::iterator it_vertex;
39270 for (it_vertex = local_vertex_nodes.begin();
39271 it_vertex != local_vertex_nodes.end();
39272 it_vertex++)
39273 {
39274 local_tmp_vector_vertex_node[counter].resize(3);
39275 local_tmp_vector_vertex_node[counter][0] = (*it_vertex)[0];
39276 local_tmp_vector_vertex_node[counter][1] = (*it_vertex)[1];
39277 local_tmp_vector_vertex_node[counter][2] = (*it_vertex)[2];
39278 counter++;
39279 }
39280
39281 // ... same for the info. related with the target areas (turn
39282 // into vector)
39283 const unsigned ntarget_areas = sorted_target_areas.size();
39284 tmp_sorted_target_areas.resize(ntarget_areas);
39285 counter = 0;
39286 std::set<Vector<double>>::iterator it_area;
39287 for (it_area = sorted_target_areas.begin();
39288 it_area != sorted_target_areas.end();
39289 ++it_area)
39290 {
39291 tmp_sorted_target_areas[counter] = (*it_area)[1];
39292 ++counter;
39293 }
39294
39295#ifdef PARANOID
39296 if (nlocal_nodes > 0 && (ntarget_areas != nlocal_nodes - 1))
39297 {
39298 std::ostringstream error_message;
39299 error_message
39300 << "The boundary (" << bound << ") was split during the "
39301 << "distribution process.\n"
39302 << "The problem comes when associating the target areas with the "
39303 << "elements that gave\nrise to the vertex coordinates.\n"
39304 << "The number of local nodes on the 'sub-polyline' ("
39305 << nlocal_nodes << ") is not according with the number of target\n"
39306 << "areas (" << ntarget_areas << ") for that number of nodes.\n"
39307 << "The target areas number must be equal to the number of "
39308 "nodes-1\n";
39309 throw OomphLibError(error_message.str(),
39310 OOMPH_CURRENT_FUNCTION,
39311 OOMPH_EXCEPTION_LOCATION);
39312 }
39313#endif
39314
39315 // The unrefinement and refinement process needs to be applied
39316 // from the bottom-left node since the internal open curve could
39317 // lie on the shared boundaries
39318 if (local_tmp_vector_vertex_node[nlocal_nodes - 1][2] <
39319 local_tmp_vector_vertex_node[0][2])
39320 {
39321 std::reverse(local_tmp_vector_vertex_node.begin(),
39322 local_tmp_vector_vertex_node.end());
39323 std::reverse(tmp_sorted_target_areas.begin(),
39324 tmp_sorted_target_areas.end());
39325 }
39326 else if (local_tmp_vector_vertex_node[nlocal_nodes - 1][2] ==
39327 local_tmp_vector_vertex_node[0][2])
39328 {
39329 if (local_tmp_vector_vertex_node[nlocal_nodes - 1][1] <
39330 local_tmp_vector_vertex_node[0][1])
39331 {
39332 std::reverse(local_tmp_vector_vertex_node.begin(),
39333 local_tmp_vector_vertex_node.end());
39334 std::reverse(tmp_sorted_target_areas.begin(),
39335 tmp_sorted_target_areas.end());
39336 }
39337 }
39338
39339 // ------------------------------------------------------------
39340 // Create the vertices along the boundary using the target
39341 // area to define the distance among them
39342 // ------------------------------------------------------------
39343
39344 // Tolerance below which the middle point can be deleted
39345 // (ratio of deflection to element length)
39346 double unrefinement_tolerance =
39347 open_curve_pt->polyline_pt(cs)->unrefinement_tolerance();
39348
39349 // Apply unrefinement
39350 bool unrefinement_applied =
39351 unrefine_boundary_constrained_by_target_area(
39352 bound,
39353 chunk,
39354 local_tmp_vector_vertex_node,
39355 unrefinement_tolerance,
39356 tmp_sorted_target_areas);
39357
39358 // Tolerance for refinement
39359 double refinement_tolerance =
39360 open_curve_pt->polyline_pt(cs)->refinement_tolerance();
39361
39362 // Apply refinement
39363 bool refinement_applied = refine_boundary_constrained_by_target_area(
39364 mesh_geom_obj_pt,
39365 local_tmp_vector_vertex_node,
39366 refinement_tolerance,
39367 tmp_sorted_target_areas);
39368
39369 // Clear the local containter to recover the nodes ordered using
39370 // the zeta value
39371 local_vertex_nodes.clear();
39372
39373 // At the end of each unrefinement/refinement step store the new
39374 // nodes on the set that will give rise to the vertices of the
39375 // new polyline representation
39376 const unsigned nnew_nodes = local_tmp_vector_vertex_node.size();
39377 for (unsigned i = 0; i < nnew_nodes; i++)
39378 {
39379 vertex_coord[0] = local_tmp_vector_vertex_node[i][0];
39380 vertex_coord[1] = local_tmp_vector_vertex_node[i][1];
39381 vertex_coord[2] = local_tmp_vector_vertex_node[i][2];
39382 vertex_nodes.insert(vertex_coord); // Global container
39383 local_vertex_nodes.insert(vertex_coord);
39384 }
39385
39386 // Update the flag to indicate whether an unrefinement or
39387 // refinement was applied
39388 update_was_performed = (unrefinement_applied || refinement_applied);
39389
39390#ifdef OOMPH_HAS_MPI
39391 if (this->is_mesh_distributed())
39392 {
39393 // Add the set of vertices for the boundary, this will help to
39394 // detect if we need to deal with sub_boundaries and
39395 // sub_polylines representations
39396 sub_vertex_nodes.push_back(local_vertex_nodes);
39397 // Increase the counter for sub_boundaries
39398 nsub_boundaries++;
39399
39400 // Mark if the polyline created by these vertices will be used
39401 // as a shared boundary or as an internal boundary
39402 if (both_root_face_elements_are_nonhalo)
39403 {
39404 internal_to_shared_boundary.push_back(false);
39405 }
39406 else
39407 {
39408 internal_to_shared_boundary.push_back(true);
39409 }
39410 }
39411#endif
39412
39413 } // while(nsorted_face_elements < nnon_halo_doubled_face_ele)
39414 // This while is in charge of sorting all the face elements to
39415 // create the new representation of the polyline (also deals
39416 // with the sub-boundary cases)
39417
39418 // Now turn into vector for ease of handling...
39419 const unsigned npoly_vertex = vertex_nodes.size();
39420 tmp_vector_vertex_node.resize(npoly_vertex);
39421 unsigned count = 0;
39422 for (std::set<Vector<double>>::iterator it = vertex_nodes.begin();
39423 it != vertex_nodes.end();
39424 ++it)
39425 {
39426 tmp_vector_vertex_node[count].resize(3);
39427 tmp_vector_vertex_node[count][0] = (*it)[0];
39428 tmp_vector_vertex_node[count][1] = (*it)[1];
39429 tmp_vector_vertex_node[count][2] = (*it)[2];
39430 ++count;
39431 }
39432
39433#ifdef OOMPH_HAS_MPI
39434 // Check that the number of set of vertices marked to be part of a
39435 // shared boundary or of an internal boundaries be the same as the
39436 // total number of sub-boundaries
39437#ifdef PARANOID
39438 const unsigned nsub_boundaries_set = sub_vertex_nodes.size();
39439 const unsigned ninternal_to_shared_boundaries =
39440 internal_to_shared_boundary.size();
39441 if (nsub_boundaries_set != ninternal_to_shared_boundaries)
39442 {
39443 std::ostringstream error_message;
39444 error_message
39445 << "The number of found sub-boundaries and the number of marked "
39446 << "internal\nboundaries are different\n"
39447 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
39448 << "Number of marked internal boundaries: ("
39449 << ninternal_to_shared_boundaries << ")\n\n";
39450 throw OomphLibError(error_message.str(),
39451 OOMPH_CURRENT_FUNCTION,
39452 OOMPH_EXCEPTION_LOCATION);
39453 }
39454#endif
39455
39456 // --------- Stuff for the sub_boundaries ----- Begin section -------
39457#ifdef PARANOID
39458 if (nsub_boundaries_set != nsub_boundaries)
39459 {
39460 std::ostringstream error_message;
39461 error_message
39462 << "The number of found sub-boundaries and the number of counted\n"
39463 << "sub-boundaries are different:\n"
39464 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
39465 << "Number of counted sub-boundaries: (" << nsub_boundaries
39466 << ")\n\n";
39467 throw OomphLibError(error_message.str(),
39468 OOMPH_CURRENT_FUNCTION,
39469 OOMPH_EXCEPTION_LOCATION);
39470 }
39471#endif
39472
39473 // Verify if need to deal with sub_boundaries
39474 if (this->is_mesh_distributed() && nsub_boundaries > 1)
39475 {
39476 // Mark the boundary as been splitted in the partition process
39477 this->Boundary_was_splitted[bound] = true;
39478
39479 // Resize the vector to store the info. of sub-boundaries
39480 sub_tmp_vector_vertex_node.resize(nsub_boundaries);
39481 // Loop over the sub-boundaries
39482 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
39483 {
39484 // Turn info. into vector for ease of handling...
39485 const unsigned nsubpoly_vertex = sub_vertex_nodes[isub].size();
39486 sub_tmp_vector_vertex_node[isub].resize(nsubpoly_vertex);
39487 unsigned subcount = 0;
39488 std::set<Vector<double>>::iterator subit;
39489 for (subit = sub_vertex_nodes[isub].begin();
39490 subit != sub_vertex_nodes[isub].end();
39491 ++subit)
39492 {
39493 sub_tmp_vector_vertex_node[isub][subcount].resize(3);
39494 sub_tmp_vector_vertex_node[isub][subcount][0] = (*subit)[0];
39495 sub_tmp_vector_vertex_node[isub][subcount][1] = (*subit)[1];
39496 sub_tmp_vector_vertex_node[isub][subcount][2] = (*subit)[2];
39497 ++subcount;
39498 }
39499 }
39500 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
39501 // --------- Stuff for the sub_boundaries ----- End section ----------
39502#endif // OOMPH_HAS_MPI
39503
39504 // For further processing the three-dimensional vector has to be
39505 // reduced to a two-dimensional vector
39506 unsigned n_vertex = tmp_vector_vertex_node.size();
39507
39508 // Resize the vector for vectices
39509 vector_vertex_node.resize(n_vertex);
39510 for (unsigned i = 0; i < n_vertex; i++)
39511 {
39512 vector_vertex_node[i].resize(2);
39513 vector_vertex_node[i][0] = tmp_vector_vertex_node[i][1];
39514 vector_vertex_node[i][1] = tmp_vector_vertex_node[i][2];
39515 }
39516
39517#ifdef OOMPH_HAS_MPI
39518 // --------- Stuff for the sub_boundaries ----- Begin section -------
39519 // Verify if need to deal with sub_boundaries
39520 if (this->is_mesh_distributed() && nsub_boundaries > 1)
39521 {
39522 // For further processing the three-dimensional vector has to be
39523 // reduced to a two-dimensional vector
39524 // Resize the vector to store the info. of sub-boundaries
39525 sub_vector_vertex_node.resize(nsub_boundaries);
39526 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
39527 {
39528 const unsigned subn_vertex = sub_tmp_vector_vertex_node[isub].size();
39529 // Resize the vector for vectices
39530 sub_vector_vertex_node[isub].resize(subn_vertex);
39531 for (unsigned i = 0; i < subn_vertex; i++)
39532 {
39533 sub_vector_vertex_node[isub][i].resize(2);
39534 sub_vector_vertex_node[isub][i][0] =
39535 sub_tmp_vector_vertex_node[isub][i][1];
39536 sub_vector_vertex_node[isub][i][1] =
39537 sub_tmp_vector_vertex_node[isub][i][2];
39538 }
39539 }
39540 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
39541
39542 // We already have the info. for the sub-boundaries (if necessary)
39543 // and then we can create the sub-boundaries representations to
39544 // ease the generation of the mesh by Triangle
39545
39546 // --------- Stuff for the sub_boundaries ----- End section ---------
39547#endif // OOMPH_HAS_MPI
39548
39549 // ------------------------------------------------------------------
39550 // Check for contiguousness
39551 // ------------------------------------------------------------------
39552#ifdef OOMPH_HAS_MPI
39553 // Only perform this checking if the mesh is not distributed When
39554 // the mesh is distributed the polylines continuity is addressed by
39555 // the sort_polylines_helper() method
39556 if (!this->is_mesh_distributed())
39557#endif
39558 {
39559 if (cs > 0)
39560 {
39561 // Final end point of previous line
39562 Vector<double> final_vertex_of_previous_segment;
39563 unsigned n_prev_vertex =
39564 open_curve_pt->curve_section_pt(cs - 1)->nvertex();
39565 final_vertex_of_previous_segment =
39566 open_curve_pt->polyline_pt(cs - 1)->vertex_coordinate(
39567 n_prev_vertex - 1);
39568
39569 unsigned prev_seg_boundary_id =
39570 open_curve_pt->curve_section_pt(cs - 1)->boundary_id();
39571
39572 // Find the error between the final vertex of the previous
39573 // line and the first vertex of the current line
39574 double error = 0.0;
39575 for (unsigned i = 0; i < 2; i++)
39576 {
39577 const double dist = final_vertex_of_previous_segment[i] -
39578 (*vector_vertex_node.begin())[i];
39579 error += dist * dist;
39580 }
39581 error = sqrt(error);
39582
39583 // If the error is bigger than the tolerance then
39584 // we probably need to reverse, but better check
39586 {
39587 // Find the error between the final vertex of the previous
39588 // line and the last vertex of the current line
39589 double rev_error = 0.0;
39590 for (unsigned i = 0; i < 2; i++)
39591 {
39592 const double dist = final_vertex_of_previous_segment[i] -
39593 (*--vector_vertex_node.end())[i];
39594 rev_error += dist * dist;
39595 }
39596 rev_error = sqrt(rev_error);
39597
39598 if (rev_error >
39600 {
39601 // It could be possible that the first segment be reversed and we
39602 // did not notice it because this check does not apply for the
39603 // first segment. We can verify if the first segment is reversed
39604 // by using the vertex number 1
39605 if (cs == 1)
39606 {
39607 // Initial end point of previous line
39608 Vector<double> initial_vertex_of_previous_segment;
39609
39610 initial_vertex_of_previous_segment =
39611 open_curve_pt->polyline_pt(cs - 1)->vertex_coordinate(0);
39612
39613 unsigned prev_seg_boundary_id =
39614 open_curve_pt->curve_section_pt(cs - 1)->boundary_id();
39615
39616 // Find the error between the initial vertex of the previous
39617 // line and the first vertex of the current line
39618 double error = 0.0;
39619 for (unsigned i = 0; i < 2; i++)
39620 {
39621 const double dist = initial_vertex_of_previous_segment[i] -
39622 (*vector_vertex_node.begin())[i];
39623 error += dist * dist;
39624 }
39625 error = sqrt(error); // Reversed only the previous one
39626
39627 // If the error is bigger than the tolerance then
39628 // we probably need to reverse, but better check
39629 if (error >
39631 {
39632 // Find the error between the final vertex of the previous
39633 // line and the last vertex of the current line
39634 double rev_error = 0.0;
39635 for (unsigned i = 0; i < 2; i++)
39636 {
39637 const double dist = initial_vertex_of_previous_segment[i] -
39638 (*--vector_vertex_node.end())[i];
39639 rev_error += dist * dist;
39640 }
39641 rev_error = sqrt(rev_error); // Reversed both the current
39642 // one and the previous one
39643
39644 if (rev_error >
39646 {
39647 std::ostringstream error_stream;
39648 error_stream
39649 << "The distance between the first node of the current\n"
39650 << "line segment (boundary " << bound
39651 << ") and either end of "
39652 << "the previous line segment\n"
39653 << "(boundary " << prev_seg_boundary_id
39654 << ") is bigger than"
39655 << " the desired tolerance "
39657 << ".\n"
39658 << "This suggests that the polylines defining the "
39659 "polygonal\n"
39660 << "representation are not properly ordered.\n"
39661 << "Fail on last vertex of polyline: ("
39662 << prev_seg_boundary_id
39663 << ") and\nfirst vertex of polyline (" << bound
39664 << ").\nThis should have failed when first trying to "
39665 << "construct the\npolygon.\n";
39666 throw OomphLibError(error_stream.str(),
39667 OOMPH_CURRENT_FUNCTION,
39668 OOMPH_EXCEPTION_LOCATION);
39669 }
39670 else
39671 {
39672 // Reverse both
39673 // Reverse the current vector to line up with the previous
39674 // one
39675 std::reverse(vector_vertex_node.begin(),
39676 vector_vertex_node.end());
39677 open_curve_pt->polyline_pt(cs - 1)->reverse();
39678 }
39679 }
39680 else
39681 {
39682 // Reverse the previous one
39683 open_curve_pt->polyline_pt(cs - 1)->reverse();
39684 }
39685
39686 } // if (cs == 1)
39687 else
39688 {
39689 std::ostringstream error_stream;
39690 error_stream
39691 << "The distance between the first node of the current\n"
39692 << "line segment (boundary " << bound
39693 << ") and either end of "
39694 << "the previous line segment\n"
39695 << "(boundary " << prev_seg_boundary_id
39696 << ") is bigger than the "
39697 << "desired tolerance "
39699 << ".\n"
39700 << "This suggests that the polylines defining the polygonal\n"
39701 << "representation are not properly ordered.\n"
39702 << "Fail on last vertex of polyline: ("
39703 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
39704 << bound << ").\n"
39705 << "This should have failed when first trying to construct\n"
39706 << "the polygon.\n";
39707 throw OomphLibError(error_stream.str(),
39708 OOMPH_CURRENT_FUNCTION,
39709 OOMPH_EXCEPTION_LOCATION);
39710 }
39711 }
39712 else
39713 {
39714 // Reverse the current vector to line up with the previous one
39715 std::reverse(vector_vertex_node.begin(),
39716 vector_vertex_node.end());
39717 }
39718 }
39719
39720 } // if (cs > 0)
39721
39722 } // if (!this->is_mesh_distributed())
39723
39724 // ---------------------------------------------------------------
39725 // Update the polylines representation
39726 // ---------------------------------------------------------------
39727 // Always update the polylines representation, in a distributed
39728 // mesh it is necessary to update the polyline representation since
39729 // it may no longer have vertices (the boundary may not be part of
39730 // the domain in the current processor)
39731
39732 // The new number of vertices
39733 n_vertex = vector_vertex_node.size();
39734
39735 // Update the polyline according to the new vertices
39736 TriangleMeshPolyLine* tmp_polyline_pt =
39737 new TriangleMeshPolyLine(vector_vertex_node, bound);
39738
39739 // Create a temporal "curve section" version of the recently
39740 // created polyline
39741 TriangleMeshCurveSection* tmp_curve_section_pt = tmp_polyline_pt;
39742
39743 // Tolerance below which the middle point can be deleted (ratio of
39744 // deflection to element length)
39745 double unrefinement_tolerance =
39746 open_curve_pt->polyline_pt(cs)->unrefinement_tolerance();
39747
39748 // Tolerance to add points
39749 double refinement_tolerance =
39750 open_curve_pt->polyline_pt(cs)->refinement_tolerance();
39751
39752 // Establish refinement and unrefinement tolerance
39753 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
39754 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
39755
39756 // Establish the maximum length constraint
39757 double maximum_length = open_curve_pt->polyline_pt(cs)->maximum_length();
39758 tmp_polyline_pt->set_maximum_length(maximum_length);
39759
39760#ifdef OOMPH_HAS_MPI
39761 // If the mesh is distributed check that the polyline still has
39762 // vertices
39763 if (this->is_mesh_distributed())
39764 {
39765 if (n_vertex >= 2)
39766 {
39767 // Pass the connection information from the old polyline to
39768 // the new one
39769 this->copy_connection_information(open_curve_pt->polyline_pt(cs),
39770 tmp_curve_section_pt);
39771 } // if (n_vertex >= 2)
39772 } // if (this->is_mesh_distributed())
39773 else
39774#endif
39775 {
39776 // Pass the connection information from the old polyline to the
39777 // new one
39778 this->copy_connection_information(open_curve_pt->polyline_pt(cs),
39779 tmp_curve_section_pt);
39780 }
39781
39782 // Now update the polyline according to the new vertices but first
39783 // check if the object is allowed to delete the representation or
39784 // if it should be done by other object
39785 bool delete_it_on_destructor = false;
39786
39787 std::set<TriangleMeshCurveSection*>::iterator it =
39788 this->Free_curve_section_pt.find(open_curve_pt->curve_section_pt(cs));
39789
39790 if (it != this->Free_curve_section_pt.end())
39791 {
39792 this->Free_curve_section_pt.erase(it);
39793 delete open_curve_pt->curve_section_pt(cs);
39794 delete_it_on_destructor = true;
39795 }
39796
39797 // -------------------------------------------------------------
39798 // Copying the new representation
39799 open_curve_pt->curve_section_pt(cs) = tmp_polyline_pt;
39800
39801 // Update the Boundary - Polyline map
39802 this->Boundary_curve_section_pt[bound] =
39803 open_curve_pt->curve_section_pt(cs);
39804
39805 if (delete_it_on_destructor)
39806 {
39807 this->Free_curve_section_pt.insert(open_curve_pt->curve_section_pt(cs));
39808 }
39809
39810#ifdef OOMPH_HAS_MPI
39811 // If there are not sub-boundaries mark the boundary if need to be
39812 // trated as shared or as internal boundary
39813 if (this->is_mesh_distributed() && nsub_boundaries == 1)
39814 {
39815 // Clear all previous stored data
39816 this->Boundary_marked_as_shared_boundary[bound].clear();
39817
39818 // .. and store the flag for the boundary
39819 this->Boundary_marked_as_shared_boundary[bound].push_back(
39820 internal_to_shared_boundary[0]);
39821 }
39822 // --------- Stuff for the sub_boundaries ----- Begin section --------
39823 // Verify if need to deal with sub_boundaries
39824 else if (this->is_mesh_distributed() && nsub_boundaries > 1)
39825 {
39826 // Create temporary representations for the boundaries, only to
39827 // create the mesh when calling Triangle
39828
39829 // Clear all previous stored data
39830 this->Boundary_subpolylines[bound].clear();
39831 // Now create storage for the sub-boundaries
39832 this->Boundary_subpolylines[bound].resize(nsub_boundaries);
39833
39834 // Clear all previous stored data
39835 this->Boundary_marked_as_shared_boundary[bound].clear();
39836 // Create storage to mark the internal boundaries as shared
39837 // boundaries
39838 this->Boundary_marked_as_shared_boundary[bound].resize(nsub_boundaries);
39839 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
39840 {
39841 // Now update the polyline according to the sub set of
39842 // vertices, set the chunk number of the polyline
39843 TriangleMeshPolyLine* sub_tmp_polyline_pt =
39844 new TriangleMeshPolyLine(sub_vector_vertex_node[isub], bound, isub);
39845
39846 // Add the sub-polyline to the container to represent the
39847 // boundary in parts
39848 this->Boundary_subpolylines[bound][isub] = sub_tmp_polyline_pt;
39849
39850 // Copy the flag that mark the boundary as internal or as
39851 // shared bound
39852 this->Boundary_marked_as_shared_boundary[bound][isub] =
39853 internal_to_shared_boundary[isub];
39854
39855 // No need to send the unrefinement/refinement and maximum
39856 // length constraints since these are only temporary
39857 // representations
39858
39859 // But we certanly we need to pass the connection information
39860 // to the sub-polylines
39861 // Get a curve section representation of the sub-polyline
39862 TriangleMeshCurveSection* tmp_sub_curve_section_pt =
39863 sub_tmp_polyline_pt;
39864 this->copy_connection_information_to_sub_polylines(
39865 tmp_curve_section_pt, tmp_sub_curve_section_pt);
39866
39867 } // for (isub < nsub_boundaries)
39868
39869 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
39870 // --------- Stuff for the sub_boundaries ----- End section ---------
39871#endif // OOMPH_HAS_MPI
39872
39873 // Delete the allocated memory for the geometric object
39874 // that represents the curvilinear boundary
39875 delete mesh_geom_obj_pt;
39876
39877 } // for (cs < ncurve_section)
39878
39879 // Cleanup the face mesh
39880 for (unsigned p = 0; p < ncurve_section; p++)
39881 {
39882 face_mesh_pt[p]->flush_node_storage();
39883 delete face_mesh_pt[p];
39884 }
39885
39886 return update_was_performed;
39887 }
39888
39889#ifdef OOMPH_HAS_MPI
39890 //======================================================================
39891 /// Updates the polylines using the elements area as
39892 /// constraint for the number of points along the boundaries
39893 //======================================================================
39894 template<class ELEMENT>
39896 Vector<TriangleMeshPolyLine*>& vector_polyline_pt,
39897 const Vector<double>& target_areas)
39898 {
39899 // Flag to check if there were a change on the shared boundary
39900 // representation
39901 unsigned update_was_performed = false;
39902
39903 // Go through all the shared boundaries/polylines
39904 const unsigned n_polylines = vector_polyline_pt.size();
39905 for (unsigned pp = 0; pp < n_polylines; pp++)
39906 {
39907 // Get the boundary id of the current polyline
39908 const unsigned shd_bnd_id = vector_polyline_pt[pp]->boundary_id();
39909
39910 // Get the chunk number
39911 const unsigned chunk = vector_polyline_pt[pp]->boundary_chunk();
39912
39913 // Get the face elements that created the shared boundary from the
39914 // bulk shared boundary elements
39915
39916 // Compute the face elements from the shared boundary elements,
39917 // create an association from the face element with the "bulk"
39918 // elements
39919 std::map<FiniteElement*, FiniteElement*> face_ele_pt_to_bulk_element_pt;
39920
39921 // The temporary storage for the halo face elements
39922 Vector<FiniteElement*> halo_shared_face_ele_pt;
39923 // The temporary storage for the nonhalo face elements
39924 Vector<FiniteElement*> nonhalo_shared_face_ele_pt;
39925
39926 // Get the number of shared boundary elements associated with the
39927 // current shared boundary
39928 const unsigned nshared_bound_ele =
39929 this->nshared_boundary_element(shd_bnd_id);
39930
39931 // Loop over the elements in the shared boundary to create the face
39932 // elements
39933 for (unsigned e = 0; e < nshared_bound_ele; e++)
39934 {
39935 // Get the shared boundary element
39936 FiniteElement* bulk_ele_pt =
39937 this->shared_boundary_element_pt(shd_bnd_id, e);
39938
39939 // Get the face index
39940 int face_index = this->face_index_at_shared_boundary(shd_bnd_id, e);
39941
39942 // Before adding the new element we need to ensure that the edge
39943 // that this element represents has not been already added
39944 FiniteElement* face_ele_pt =
39945 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
39946
39947 // Establish the association between the bulk element and the
39948 // face element
39949 face_ele_pt_to_bulk_element_pt[face_ele_pt] = bulk_ele_pt;
39950
39951 // Nonhalo element
39952 if (!bulk_ele_pt->is_halo())
39953 {
39954 // Add nonhalo shared face element to the container
39955 nonhalo_shared_face_ele_pt.push_back(face_ele_pt);
39956 }
39957 else // halo element
39958 {
39959 // Add halo shared face element to the container
39960 halo_shared_face_ele_pt.push_back(face_ele_pt);
39961 }
39962
39963 } // for (e < nshared_bound_ele)
39964
39965 // Now we have the face elements, we need to ensure that the halo
39966 // and nonhalo bulk element are sorted one after the other
39967 Vector<Vector<FiniteElement*>> unsorted_shared_bulk_ele_pt;
39968
39969 // Mark the face elements already used
39970 std::map<FiniteElement*, bool> shared_face_done;
39971
39972 // Get the number of nonhalo face elements
39973 const unsigned nnonhalo_face_shared_ele =
39974 nonhalo_shared_face_ele_pt.size();
39975
39976 // Get the number of halo face elements
39977 const unsigned nhalo_face_shared_ele = halo_shared_face_ele_pt.size();
39978
39979#ifdef PARANOID
39980 // The number of nonhalo shared face boundary elements must be the
39981 // half of the total number of shared boundary elements
39982 if (nshared_bound_ele / 2 != nnonhalo_face_shared_ele)
39983 {
39984 std::ostringstream error_message;
39985 error_message
39986 << "The number of shared boundary elements (" << nshared_bound_ele
39987 << ") is not the double\nof the number of unsorted NONHALO shared "
39988 << "face boundary elements (" << nnonhalo_face_shared_ele << ")\n"
39989 << "for the current boundary (" << shd_bnd_id << ")\n\n";
39990 throw OomphLibError(error_message.str(),
39991 OOMPH_CURRENT_FUNCTION,
39992 OOMPH_EXCEPTION_LOCATION);
39993 }
39994
39995 // The number of halo shared face boundary elements must be the
39996 // half of the total number of shared boundary elements
39997 if (nshared_bound_ele / 2 != nhalo_face_shared_ele)
39998 {
39999 std::ostringstream error_message;
40000 error_message
40001 << "The number of shared boundary elements (" << nshared_bound_ele
40002 << ") is not the double\nof the number of unsorted HALO shared "
40003 << "face boundary elements (" << nhalo_face_shared_ele << ")\n"
40004 << "for the current boundary (" << shd_bnd_id << ")\n\n";
40005 throw OomphLibError(error_message.str(),
40006 OOMPH_CURRENT_FUNCTION,
40007 OOMPH_EXCEPTION_LOCATION);
40008 }
40009#endif
40010
40011 // ------------------------------------------------------------------
40012 // Loop over the nonhalo face elements and look for the halo face
40013 // element at the other side of the shared boundary
40014 for (unsigned inh = 0; inh < nnonhalo_face_shared_ele; inh++)
40015 {
40016 // Get the inh-th face element
40017 FiniteElement* nonhalo_face_ele_pt = nonhalo_shared_face_ele_pt[inh];
40018
40019 // Get the number of nodes on the face element
40020 const unsigned nnodes_nh = nonhalo_face_ele_pt->nnode();
40021 // Get the first and last node on the element
40022 Node* nh_first_node_pt = nonhalo_face_ele_pt->node_pt(0);
40023 Node* nh_last_node_pt = nonhalo_face_ele_pt->node_pt(nnodes_nh - 1);
40024
40025 // Now find the (halo) face element at the other side of the
40026 // shared boundary
40027 for (unsigned ih = 0; ih < nhalo_face_shared_ele; ih++)
40028 {
40029 // Get the ih-th face element
40030 FiniteElement* halo_face_ele_pt = halo_shared_face_ele_pt[ih];
40031
40032 // Check that the face element has not been done
40033 if (!shared_face_done[halo_face_ele_pt])
40034 {
40035 // Get the number of nodes on the face element
40036 const unsigned nnodes_h = halo_face_ele_pt->nnode();
40037 // Get the first and last node on the element
40038 Node* h_first_node_pt = halo_face_ele_pt->node_pt(0);
40039 Node* h_last_node_pt = halo_face_ele_pt->node_pt(nnodes_h - 1);
40040
40041 // If the nodes are the same then we have found the (halo)
40042 // face element at the other side of the shared boundary
40043 if (nh_first_node_pt == h_first_node_pt &&
40044 nh_last_node_pt == h_last_node_pt)
40045 {
40046 // Get the BULK elements associated with the face elements
40047 Vector<FiniteElement*> tmp_bulk_element_pt;
40048 // Get the BULK elements associated to the face elements
40049 // (the nonhalo and the halo)
40050 FiniteElement* nonhalo_bulk_ele_pt =
40051 face_ele_pt_to_bulk_element_pt[nonhalo_face_ele_pt];
40052 FiniteElement* halo_bulk_ele_pt =
40053 face_ele_pt_to_bulk_element_pt[halo_face_ele_pt];
40054
40055 // Add the BULK elements to the temporal storage
40056 tmp_bulk_element_pt.push_back(nonhalo_bulk_ele_pt);
40057 tmp_bulk_element_pt.push_back(halo_bulk_ele_pt);
40058
40059 // Store the pair of elements associated to the "edge"
40060 unsorted_shared_bulk_ele_pt.push_back(tmp_bulk_element_pt);
40061
40062 // Mark the face elements as done
40063 shared_face_done[nonhalo_face_ele_pt] = true;
40064 shared_face_done[halo_face_ele_pt] = true;
40065
40066 // Break the loop for (ih < nhalo_face_shared_ele)
40067 break;
40068 } // if (nh_first_node_pt == h_first_node_pt &&
40069 // nh_last_node_pt == h_last_node_pt)
40070 else if (nh_first_node_pt == h_last_node_pt &&
40071 nh_last_node_pt == h_first_node_pt)
40072 {
40073 // Get the BULK elements associated with the face elements
40074 Vector<FiniteElement*> tmp_bulk_element_pt;
40075 // Get the BULK elements associated to the face elements
40076 // (the nonhalo and the halo)
40077 FiniteElement* nonhalo_bulk_ele_pt =
40078 face_ele_pt_to_bulk_element_pt[nonhalo_face_ele_pt];
40079 FiniteElement* halo_bulk_ele_pt =
40080 face_ele_pt_to_bulk_element_pt[halo_face_ele_pt];
40081
40082 // Add the BULK elements to the temporal storage
40083 tmp_bulk_element_pt.push_back(nonhalo_bulk_ele_pt);
40084 tmp_bulk_element_pt.push_back(halo_bulk_ele_pt);
40085
40086 // Store the pair of elements associated to the "edge"
40087 unsorted_shared_bulk_ele_pt.push_back(tmp_bulk_element_pt);
40088
40089 // Mark the face elements as done
40090 shared_face_done[nonhalo_face_ele_pt] = true;
40091 shared_face_done[halo_face_ele_pt] = true;
40092
40093 // Break the loop for (ih < nhalo_face_shared_ele)
40094 break;
40095 } // else if (nh_first_node_pt == h_last_node_pt &&
40096 // nh_last_node_pt == h_first_node_pt)
40097
40098 } // if (face_done[halo_face_ele_pt])
40099
40100 } // for (ih < nhalo_face_shared_ele)
40101
40102 } // for (inh < nnonhalo_face_shared_ele)
40103
40104 // -------------------------------------------------------------
40105 // Now sort the face elements
40106 // -------------------------------------------------------------
40107
40108 // We already have the shared face elements that make the shared
40109 // boundary (and the bulk elements), now sort them to create a
40110 // contiguous boundary
40111
40112#ifdef PARANOID
40113 const unsigned nunsorted_shared_bulk_ele =
40114 unsorted_shared_bulk_ele_pt.size();
40115
40116 // The number of unsorted shared BULK elements MUST be the same
40117 // as the number of shared_boundary elements divided by two
40118 if (nshared_bound_ele / 2 != nunsorted_shared_bulk_ele)
40119 {
40120 std::ostringstream error_message;
40121 error_message
40122 << "The number of shared boundary elements (" << nshared_bound_ele
40123 << ") is not the double\nof the number of unsorted shared bulk "
40124 << "boundary elements (" << nunsorted_shared_bulk_ele << ")\n"
40125 << "for the current boundary (" << shd_bnd_id << ")\n\n";
40126 throw OomphLibError(error_message.str(),
40127 OOMPH_CURRENT_FUNCTION,
40128 OOMPH_EXCEPTION_LOCATION);
40129 }
40130
40131 // The number of done shared face elements MUST be the same as the
40132 // sum of the nonhalo and halo shared boundary face elements
40133 if ((nnonhalo_face_shared_ele + nhalo_face_shared_ele) !=
40134 shared_face_done.size())
40135 {
40136 std::ostringstream error_message;
40137 error_message << "The number of DONE shared boundary face elements ("
40138 << shared_face_done.size()
40139 << ") is not the same\n as the sum of"
40140 << "the nonhalo face shared boundary elements ("
40141 << nnonhalo_face_shared_ele
40142 << ")\nand the halo face shared "
40143 << "boundary elements (" << nhalo_face_shared_ele
40144 << ") for the\n/"
40145 << "current boundary (" << shd_bnd_id << ")\n\n";
40146 throw OomphLibError(error_message.str(),
40147 OOMPH_CURRENT_FUNCTION,
40148 OOMPH_EXCEPTION_LOCATION);
40149 }
40150#endif
40151
40152 // Clear the already done face elements
40153 shared_face_done.clear();
40154
40155 // The number of sorted face elements
40156 unsigned nsorted_face_ele = 0;
40157
40158 // Storing for the sorting nodes extracted from the face
40159 // elements. This are also used to update the polyline
40160 std::list<Node*> sorted_nodes;
40161
40162 // Storing for the sorted shared face elements
40163 std::list<FiniteElement*> sorted_shared_bound_elements_pt;
40164
40165 // Get the root face element
40166 FiniteElement* root_face_ele_pt = nonhalo_shared_face_ele_pt[0];
40167 nsorted_face_ele++;
40168
40169 // Mark face as done
40170 shared_face_done[root_face_ele_pt] = true;
40171
40172 // The initial and final node on the list
40173 const unsigned nnodes_root = root_face_ele_pt->nnode();
40174 Node* first_node_pt = root_face_ele_pt->node_pt(0);
40175 Node* last_node_pt = root_face_ele_pt->node_pt(nnodes_root - 1);
40176
40177 // Push back on the list the new nodes
40178 sorted_nodes.push_back(first_node_pt);
40179 sorted_nodes.push_back(last_node_pt);
40180
40181 // Store the bulk elements of the current face
40182 sorted_shared_bound_elements_pt.push_back(
40183 unsorted_shared_bulk_ele_pt[0][0]);
40184 sorted_shared_bound_elements_pt.push_back(
40185 unsorted_shared_bulk_ele_pt[0][1]);
40186
40187 // Sort the face elements
40188 while (nsorted_face_ele < nnonhalo_face_shared_ele)
40189 {
40190 // Flag to indicate when a node was added
40191 bool node_added = false;
40192
40193 // Start from the next edge since we have already added the
40194 // previous one as the initial face element
40195 for (unsigned iface = 1; iface < nnonhalo_face_shared_ele; iface++)
40196 {
40197 FiniteElement* tmp_shared_face_ele_pt =
40198 nonhalo_shared_face_ele_pt[iface];
40199
40200 // If face has not been sorted
40201 if (!shared_face_done[tmp_shared_face_ele_pt])
40202 {
40203 // Get the number of nodes for the current face element
40204 const unsigned tmp_nnodes = tmp_shared_face_ele_pt->nnode();
40205
40206 // Get each individual node
40207 Node* left_node_pt = tmp_shared_face_ele_pt->node_pt(0);
40208 Node* right_node_pt =
40209 tmp_shared_face_ele_pt->node_pt(tmp_nnodes - 1);
40210
40211 if (left_node_pt == first_node_pt)
40212 {
40213 // Push front the new node
40214 sorted_nodes.push_front(right_node_pt);
40215 first_node_pt = right_node_pt;
40216 node_added = true;
40217
40218 // Store the elements of the current face element
40219 sorted_shared_bound_elements_pt.push_front(
40220 unsorted_shared_bulk_ele_pt[iface][1]);
40221 sorted_shared_bound_elements_pt.push_front(
40222 unsorted_shared_bulk_ele_pt[iface][0]);
40223 }
40224 else if (left_node_pt == last_node_pt)
40225 {
40226 // Push back the new node
40227 sorted_nodes.push_back(right_node_pt);
40228 last_node_pt = right_node_pt;
40229 node_added = true;
40230
40231 // Store the elements of the current face element
40232 sorted_shared_bound_elements_pt.push_back(
40233 unsorted_shared_bulk_ele_pt[iface][0]);
40234 sorted_shared_bound_elements_pt.push_back(
40235 unsorted_shared_bulk_ele_pt[iface][1]);
40236 }
40237 else if (right_node_pt == first_node_pt)
40238 {
40239 // Push front the new node
40240 sorted_nodes.push_front(left_node_pt);
40241 first_node_pt = left_node_pt;
40242 node_added = true;
40243
40244 // Store the elements of the current face element
40245 sorted_shared_bound_elements_pt.push_front(
40246 unsorted_shared_bulk_ele_pt[iface][1]);
40247 sorted_shared_bound_elements_pt.push_front(
40248 unsorted_shared_bulk_ele_pt[iface][0]);
40249 }
40250 else if (right_node_pt == last_node_pt)
40251 {
40252 // Push back the new node
40253 sorted_nodes.push_back(left_node_pt);
40254 last_node_pt = left_node_pt;
40255 node_added = true;
40256
40257 // Store the elements of the current face element
40258 sorted_shared_bound_elements_pt.push_back(
40259 unsorted_shared_bulk_ele_pt[iface][0]);
40260 sorted_shared_bound_elements_pt.push_back(
40261 unsorted_shared_bulk_ele_pt[iface][1]);
40262 }
40263
40264 if (node_added)
40265 {
40266 // Mark as done if one of its nodes has been added to the
40267 // list
40268 shared_face_done[tmp_shared_face_ele_pt] = true;
40269 nsorted_face_ele++;
40270
40271 // Break the for
40272 break;
40273 }
40274
40275 } // if (!shared_face_done[tmp_shared_face_ele_pt])
40276
40277 } // for (iface < nnonhalo_face_shared_ele)
40278
40279 } // while (nsorted_face_ele < nnonhalo_face_shared_ele))
40280
40281 // ----------------------------------------------------------------
40282 // Here we can safely delete the face elements, they are no longer
40283 // required
40284
40285 // First the nonhalo face elements
40286 for (unsigned inh = 0; inh < nnonhalo_face_shared_ele; inh++)
40287 {
40288 delete nonhalo_shared_face_ele_pt[inh];
40289 nonhalo_shared_face_ele_pt[inh] = 0;
40290 } // for (inh < nnonhalo_face_shared_ele)
40291
40292 // ... then the halo face elements
40293 for (unsigned ih = 0; ih < nhalo_face_shared_ele; ih++)
40294 {
40295 delete halo_shared_face_ele_pt[ih];
40296 halo_shared_face_ele_pt[ih] = 0;
40297 } // for (inh < nhalo_face_shared_ele)
40298
40299 // ------------------------------------------------------------------
40300 // At this point we already have a sorted list of nodes, get the
40301 // vertices from them and store them in a vector container
40302
40303 // Get the number of nodes on the list
40304 const unsigned n_nodes = sorted_nodes.size();
40305
40306 // The vector to store the vertices
40307 Vector<Vector<double>> polyline_vertices(n_nodes);
40308
40309 // Copy the vertices from the nodes
40310 unsigned counter = 0;
40311 for (std::list<Node*>::iterator it_nodes = sorted_nodes.begin();
40312 it_nodes != sorted_nodes.end();
40313 it_nodes++)
40314 {
40315 polyline_vertices[counter].resize(2);
40316 polyline_vertices[counter][0] = (*it_nodes)->x(0);
40317 polyline_vertices[counter][1] = (*it_nodes)->x(1);
40318 counter++;
40319 }
40320
40321 // ------------------------------------------------------------------
40322 // Now get the target areas associated to the shared boundary
40323 // elements
40324
40325 // Copy the sorted elements in a vector
40326 Vector<FiniteElement*> sorted_shared_ele_pt;
40327 for (std::list<FiniteElement*>::iterator it_ele =
40328 sorted_shared_bound_elements_pt.begin();
40329 it_ele != sorted_shared_bound_elements_pt.end();
40330 it_ele++)
40331 {
40332 sorted_shared_ele_pt.push_back((*it_ele));
40333 }
40334
40335 // Get the number of target areas
40336 const unsigned n_shared_target_areas = sorted_shared_ele_pt.size();
40337 Vector<double> sorted_shared_target_areas(n_shared_target_areas);
40338
40339 // Mark those shared elements already found
40340 std::map<std::pair<GeneralisedElement*, unsigned>, bool> shared_ele_done;
40341
40342 // Counter for the number of already done shared elements
40343 unsigned count_found_shared_element = 0;
40344
40345 // Get the target area associated to the shared boundary elements
40346 const unsigned nele = this->nelement();
40347
40348 // Loop over the elements to find the target areas associated to
40349 // the shared boundary elements
40350 for (unsigned e = 0; e < nele; e++)
40351 {
40352 GeneralisedElement* current_ele_pt = this->element_pt(e);
40353 // Now compare the current element with those in the sorted
40354 // shared element array
40355 for (unsigned s = 0; s < n_shared_target_areas; s++)
40356 {
40357 // Get the element
40358 GeneralisedElement* current_shared_ele_pt = sorted_shared_ele_pt[s];
40359 // Create the pair element-index to check if done
40360 std::pair<GeneralisedElement*, unsigned> pair_gen_ele_idx =
40361 std::make_pair(current_shared_ele_pt, s);
40362 if (!shared_ele_done[pair_gen_ele_idx])
40363 {
40364 // Compare with the global element
40365 if (current_ele_pt == current_shared_ele_pt)
40366 {
40367 // Store the target area of the current shared element
40368 sorted_shared_target_areas[s] = target_areas[e];
40369 // Mark the shared element as done
40370 shared_ele_done[pair_gen_ele_idx] = true;
40371 // Increase the number of found elements
40372 count_found_shared_element++;
40373 } // if (current_ele_pt == current_shared_ele_pt)
40374 } // if (!shared_ele_done[current_shared_ele_pt])
40375 } // for (s < nshared_taget_areas)
40376
40377 // Check if all shared elements have been found
40378 if (count_found_shared_element == n_shared_target_areas)
40379 {
40380 break;
40381 }
40382
40383 } // for (e < nele)
40384
40385#ifdef PARANOID
40386 // Check if the number of found target areas is the same as the
40387 // number of shared target areas
40388 if (count_found_shared_element != n_shared_target_areas)
40389 {
40390 std::ostringstream error_message;
40391 error_message << "The number of found target areas ("
40392 << count_found_shared_element
40393 << ") is different from the "
40394 << "total number\nof target areas ("
40395 << n_shared_target_areas << ") in shared boundary ("
40396 << shd_bnd_id << ")\n\n";
40397 throw OomphLibError(error_message.str(),
40398 OOMPH_CURRENT_FUNCTION,
40399 OOMPH_EXCEPTION_LOCATION);
40400 }
40401#endif
40402
40403 // The number of vertices
40404 const unsigned n_vertices = n_nodes;
40405
40406 // Get the number of segments from the input vector_polyline_pt
40407 const unsigned n_segments = vector_polyline_pt[pp]->nsegment();
40408 // Get the number of segments from the input vector_polyline_pt to
40409 // ensure that the shared boundary corresponds to the one
40410 // represented by the shared face elements (this has sence when the
40411 // mesh was re-created from re-starting)
40412
40413 // Check that the number of vertices correspond with the number of
40414 // segments
40415#ifdef PARANOID
40416 if (n_segments != n_vertices - 1)
40417 {
40418 std::ostringstream error_message;
40419 error_message
40420 << "The number of segments from the current shared polyline "
40421 << "(" << n_segments << ") does not\ncorrespond with the number of "
40422 << "sorted vertices (" << n_vertices - 1
40423 << ") of the current shared\n"
40424 << "boundary\n\n";
40425 throw OomphLibError(error_message.str(),
40426 OOMPH_CURRENT_FUNCTION,
40427 OOMPH_EXCEPTION_LOCATION);
40428 }
40429
40430 // Check that the number of target areas correspond with the number
40431 // of vertices
40432 if (n_segments != n_shared_target_areas / 2)
40433 {
40434 std::ostringstream error_message;
40435 error_message
40436 << "The number of segments for the current sorting of edges "
40437 << "(" << n_segments << ") is different\nfrom the number of "
40438 << "target areas (" << n_shared_target_areas / 2 << ")\n\n";
40439 throw OomphLibError(error_message.str(),
40440 OOMPH_CURRENT_FUNCTION,
40441 OOMPH_EXCEPTION_LOCATION);
40442 }
40443#endif
40444
40445 // ------------------------------------------------------------------
40446 // Get the target areas that are used to perform the unrefinement
40447 // and refinement operation. For each face element on a shared
40448 // polyline there are two bulk elements, a halo and a haloed
40449 // element, each with an associated target area. Review the
40450 // function
40451 // TriangleMesh::create_polylines_from_halo_elements_helper() to
40452 // check how the shared boundaries were created
40453 Vector<double> polyline_target_area(n_segments);
40454 // Loop over the segments in the shared polyline
40455 for (unsigned s = 0; s < n_segments; s++)
40456 {
40457 // Get the minimum of the associated target areas
40458 polyline_target_area[s] =
40459 std::min(sorted_shared_target_areas[s * 2],
40460 sorted_shared_target_areas[(s * 2) + 1]);
40461 }
40462
40463 // Before going to the unrefinement or refinement process check
40464 // that in all processors where the shared boundary lives start
40465 // from the same vertex.
40466 // Start from the bottom left vertex
40467 if (polyline_vertices[n_vertices - 1][1] < polyline_vertices[0][1])
40468 {
40469 std::reverse(polyline_vertices.begin(), polyline_vertices.end());
40470 std::reverse(polyline_target_area.begin(), polyline_target_area.end());
40471 }
40472 else if (polyline_vertices[n_vertices - 1][1] == polyline_vertices[0][1])
40473 {
40474 if (polyline_vertices[n_vertices - 1][0] < polyline_vertices[0][0])
40475 {
40476 std::reverse(polyline_vertices.begin(), polyline_vertices.end());
40477 std::reverse(polyline_target_area.begin(),
40478 polyline_target_area.end());
40479 }
40480 }
40481
40482 // ------------------------------------------------------------------
40483 // Apply unrefinement
40484 bool unrefinement_applied = false;
40485 // Apply unefinement if there are more than three nodes at the
40486 // shared boundary
40487 if (n_vertices > 3)
40488 {
40489 unrefinement_applied =
40490 unrefine_shared_boundary_constrained_by_target_area(
40491 shd_bnd_id, chunk, polyline_vertices, polyline_target_area);
40492 }
40493
40494 // Apply refinement
40495 bool refinement_applied =
40496 refine_shared_boundary_constrained_by_target_area(polyline_vertices,
40497 polyline_target_area);
40498
40499 // Was unrefinement/refinement applied
40500 update_was_performed |= (unrefinement_applied || refinement_applied);
40501
40502 // ------------------------------------------------------------------
40503 // Update the polyline representation of the shared boundary
40504
40505 // The new shared polyline representation
40506 TriangleMeshPolyLine* new_polyline_pt =
40507 new TriangleMeshPolyLine(polyline_vertices, shd_bnd_id);
40508
40509 // Get the curve section representation
40510 TriangleMeshCurveSection* curve_section_pt = vector_polyline_pt[pp];
40511
40512 // Copy the connection information from the old shared polyline to
40513 // the new one
40514 this->copy_connection_information(curve_section_pt, new_polyline_pt);
40515
40516 // Now update the polyline according to the new vertices but first
40517 // check if the object is allowed to delete the representation or
40518 // if it should be done by other object
40519 bool delete_it_on_destructor = false;
40520
40521 // Establish the element as being deleted by the destructor of the
40522 // class
40523 std::set<TriangleMeshCurveSection*>::iterator it =
40524 this->Free_curve_section_pt.find(curve_section_pt);
40525
40526 if (it != this->Free_curve_section_pt.end())
40527 {
40528 this->Free_curve_section_pt.erase(it);
40529 delete curve_section_pt;
40530 delete_it_on_destructor = true;
40531 }
40532
40533 // Copy the new representation to the output vector_polyline_pt
40534 vector_polyline_pt[pp] = new_polyline_pt;
40535
40536 // Get the new curve section representation
40537 TriangleMeshCurveSection* new_curve_section_pt = vector_polyline_pt[pp];
40538
40539 // Update the Boundary - Polyline map
40540 this->Boundary_curve_section_pt[shd_bnd_id] = new_curve_section_pt;
40541
40542 if (delete_it_on_destructor)
40543 {
40544 this->Free_curve_section_pt.insert(new_curve_section_pt);
40545 }
40546
40547 } // for (pp < npoly)
40548
40549 return update_was_performed;
40550 }
40551#endif // #ifdef OOMPH_HAS_MPI
40552
40553 //=========================================================================
40554 /// Helper function that performs the unrefinement process
40555 /// on the specified boundary by using the provided vertices
40556 /// representation and the associated target area.
40557 //=========================================================================
40558 template<class ELEMENT>
40561 const unsigned& b,
40562 const unsigned& c,
40563 Vector<Vector<double>>& vector_bnd_vertices,
40564 double& unrefinement_tolerance,
40565 Vector<double>& area_constraint)
40566 {
40567 // Store the vertices not allowed for deletion
40568 std::set<Vector<double>> no_delete_vertex;
40569
40570 // Does the boundary receives connections?
40571 const bool boundary_receive_connections =
40572 this->boundary_connections(b, c, no_delete_vertex);
40573
40574 // Boolean that indicates whether an actual update of the vertex
40575 // coordinates was performed
40576 bool unrefinement_applied = false;
40577
40578 // Return inmedately
40579 if (!Do_boundary_unrefinement_constrained_by_target_areas)
40580 {
40581 return unrefinement_applied;
40582 }
40583
40584 // Strategy to delete nodes: Consider the target area of the
40585 // elements (e_i and e_(i+1)) sharing the i-th node (middle node),
40586 // if the number of segments to be added is equal to zero for both
40587 // elements then compute the average of both target areas and check
40588 // if the number of segments is still zero, if that holds mark the
40589 // node to be deleted. Before delete the node check whether it is in
40590 // the non_delete_vertex list. Skip the i+1-th node and go for the
40591 // (i+2)-th one, it means, increase the counter for current node by
40592 // two.
40593
40594 // Number of vertices on the boundary
40595 unsigned n_vertex = vector_bnd_vertices.size();
40596
40597 // Compute a constant value
40598 const double constant_value = 4.0 / sqrt(3.0);
40599
40600 if (n_vertex > 2)
40601 {
40602 // Go through all the vertices and delete points when the target area
40603 // indicates zero points along the boundary
40604 for (unsigned i = 1; i < n_vertex - 1; i += 2)
40605 {
40606 if (area_constraint[i - 1] > 0 && area_constraint[i] > 0)
40607 {
40608 const double local_zeta_first = vector_bnd_vertices[i - 1][0];
40609 const double local_zeta_last = vector_bnd_vertices[i + 1][0];
40610 const double local_length_zeta =
40611 std::fabs(local_zeta_last - local_zeta_first);
40612
40613 const double x1 = vector_bnd_vertices[i - 1][1];
40614 const double y1 = vector_bnd_vertices[i - 1][2];
40615 const double x2 = vector_bnd_vertices[i + 1][1];
40616 const double y2 = vector_bnd_vertices[i + 1][2];
40617 const double local_length =
40618 sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
40619
40620 const double x_m = vector_bnd_vertices[i][1];
40621 const double y_m = vector_bnd_vertices[i][2];
40622
40623 const double average_area_constraint =
40624 (area_constraint[i - 1] + area_constraint[i]) / 2.0;
40625
40626 // Compute the length of the the side of an equilateral
40627 // triangle
40628 const double length_side =
40629 sqrt(constant_value * average_area_constraint);
40630
40631 const double length_side_zeta =
40632 (local_length_zeta * length_side) / local_length;
40633
40634 // Is the new length greater that the old one
40635 if ((length_side_zeta / local_length_zeta) > 1.0)
40636 {
40637 // If the number of segments is zero then verify the condition for
40638 // deletion of nodes but using the condition in the default
40639 // unrefine_boundary() method. If both conditions are true then
40640 // delete the node
40641 // Maths from
40642 // http://www.cgafaq.info/wiki/Circle_Through_Three_Points
40643 double a_x = vector_bnd_vertices[i - 1][1];
40644 double a_y = vector_bnd_vertices[i - 1][2];
40645 double b_x = vector_bnd_vertices[i][1];
40646 double b_y = vector_bnd_vertices[i][2];
40647 double c_x = vector_bnd_vertices[i + 1][1];
40648 double c_y = vector_bnd_vertices[i + 1][2];
40649
40650 double a = b_x - a_x;
40651 double b = b_y - a_y;
40652 double c = c_x - a_x;
40653 double d = c_y - a_y;
40654
40655 double e = a * (a_x + b_x) + b * (a_y + b_y);
40656 double f = c * (a_x + c_x) + d * (a_y + c_y);
40657
40658 double g = 2.0 * (a * (c_y - b_y) - b * (c_x - b_x));
40659
40660 bool do_it = false;
40661 if (std::fabs(g) < 1.0e-14)
40662 {
40663 do_it = true;
40664 }
40665 else
40666 {
40667 double p_x = (d * e - b * f) / g;
40668 double p_y = (a * f - c * e) / g;
40669
40670 double r = sqrt(pow((a_x - p_x), 2) + pow((a_y - p_y), 2));
40671
40672 double rhalfca_x = 0.5 * (a_x - c_x);
40673 double rhalfca_y = 0.5 * (a_y - c_y);
40674
40675 double halfca_squared = pow(rhalfca_x, 2) + pow(rhalfca_y, 2);
40676
40677 double sticky_out_bit =
40678 r - sqrt(std::fabs((r * r) - halfca_squared));
40679
40680 // If sticky out bit divided by distance between end nodes
40681 // is less than tolerance the boundary is so flat that we
40682 // can safely kill the node
40683 if ((sticky_out_bit / (2.0 * sqrt(halfca_squared))) <
40684 unrefinement_tolerance)
40685 {
40686 do_it = true;
40687 }
40688 }
40689
40690 // If the vertex was proposed for deletion check if it is
40691 // allowed for being deleted
40692 if (do_it && boundary_receive_connections)
40693 {
40694 // Is the vertex one of the non deletable vertices
40695 for (std::set<Vector<double>>::iterator it =
40696 no_delete_vertex.begin();
40697 it != no_delete_vertex.end();
40698 it++)
40699 {
40700 // Compute the distance between the proposed node to
40701 // delete and the ones that should not be deleted
40702 const double x = (*it)[0];
40703 const double y = (*it)[1];
40704 double error = (x_m - x) * (x_m - x) + (y_m - y) * (y_m - y);
40705 error = sqrt(error);
40706
40707 if (error <
40709 {
40710 // Do not delete the vertex
40711 do_it = false;
40712 break;
40713 }
40714 }
40715
40716 } // if (do_it && boundary_receive_connections)
40717
40718 // Remove node?
40719 if (do_it)
40720 {
40721 vector_bnd_vertices[i].resize(0);
40722 }
40723 } // if (n_seg == 0)
40724 } // if (area_constraint[i] >= 0)
40725 } // for (i < n_vertex-1)
40726
40727 // Create a new (temporary) vector for the nodes, so that deleted nodes
40728 // are not stored
40729 Vector<Vector<double>> compact_vector;
40730
40731 // Compact vector for target areas too
40732 Vector<double> compact_area_constraint;
40733
40734 // Copy only the non deleted nodes
40735 for (unsigned i = 0; i < n_vertex; i++)
40736 {
40737 // If the entry was not deleted include it in the new vector
40738 if (vector_bnd_vertices[i].size() != 0)
40739 {
40740 compact_vector.push_back(vector_bnd_vertices[i]);
40741 }
40742 }
40743
40744 // ------------------------------------------------------------------
40745 // Size of the target areas vector
40746 unsigned nsize_target = area_constraint.size();
40747 if (nsize_target == 1)
40748 {
40749 // No node was deleted, just copy the target area
40750 compact_area_constraint.push_back(area_constraint[0]);
40751 }
40752
40753 // Copy the target areas
40754 for (unsigned i = 1; i < n_vertex; i += 2)
40755 {
40756 // If the entry was not deleted include the target areas of both
40757 // elements sharing the node
40758 if (vector_bnd_vertices[i].size() != 0)
40759 {
40760 compact_area_constraint.push_back(area_constraint[i - 1]);
40761 // To catch the case when working with even number of vertex
40762 if (i < nsize_target)
40763 {
40764 compact_area_constraint.push_back(area_constraint[i]);
40765 }
40766 }
40767 else
40768 {
40769 // If the node was deleted then compute the new target area as the
40770 // average of the target area of the elements sharing the node
40771 double new_area_constraint =
40772 (area_constraint[i - 1] + area_constraint[i]) / 2.0;
40773 compact_area_constraint.push_back(new_area_constraint);
40774 }
40775 }
40776
40777 // If the size of the compact vector is different from the size of the
40778 // vector before applying the area length constraint then the polyline
40779 // was updated
40780 if (n_vertex != compact_vector.size())
40781 {
40782 unrefinement_applied = true;
40783 }
40784
40785 // Copy back to the original vector
40786 n_vertex = compact_vector.size();
40787 vector_bnd_vertices.resize(n_vertex);
40788 for (unsigned i = 0; i < n_vertex; i++)
40789 {
40790 vector_bnd_vertices[i].resize(3);
40791 vector_bnd_vertices[i][0] = compact_vector[i][0];
40792 vector_bnd_vertices[i][1] = compact_vector[i][1];
40793 vector_bnd_vertices[i][2] = compact_vector[i][2];
40794 }
40795
40796 // Copy back to the original vector of target areas
40797 unsigned ntarget_areas = compact_area_constraint.size();
40798 area_constraint.resize(ntarget_areas);
40799 for (unsigned i = 0; i < ntarget_areas; i++)
40800 {
40801 area_constraint[i] = compact_area_constraint[i];
40802 }
40803
40804 } // if (n_vertex > 2)
40805
40806 return unrefinement_applied;
40807 }
40808
40809 //=========================================================================
40810 /// Helper function that performs the refinement process
40811 /// on the specified boundary by using the provided vertices
40812 /// representation and the associated elements target area.
40813 //=========================================================================
40814 template<class ELEMENT>
40817 MeshAsGeomObject* mesh_geom_obj_pt,
40818 Vector<Vector<double>>& vector_bnd_vertices,
40819 double& refinement_tolerance,
40820 Vector<double>& area_constraint)
40821 {
40822 // Boolean that indicates whether an actual update of the vertex
40823 // coordinates was performed
40824 bool refinement_applied = false;
40825
40826 // Return inmedately
40827 if (!Do_boundary_refinement_constrained_by_target_areas)
40828 {
40829 return refinement_applied;
40830 }
40831
40832 // Get the total number of current vertices
40833 unsigned n_vertex = vector_bnd_vertices.size();
40834
40835 // Compute a constant value
40836 const double constant_value = 4.0 / sqrt(3.0);
40837
40838 if (n_vertex > 1)
40839 {
40840 // Create a new (temporary) vector for the nodes, so that new
40841 // nodes can be stored
40842 Vector<Vector<double>> new_vector;
40843
40844 // Go through all the vertices and create points according to the
40845 // specified element area
40846 for (unsigned i = 0; i < n_vertex - 1; i++)
40847 {
40848 // Include the first node
40849 new_vector.push_back(vector_bnd_vertices[i]);
40850
40851 if (area_constraint[i] > 0)
40852 {
40853 double local_zeta_first = vector_bnd_vertices[i][0];
40854 double local_zeta_last = vector_bnd_vertices[i + 1][0];
40855 const double local_length_zeta =
40856 std::fabs(local_zeta_last - local_zeta_first);
40857
40858 // Check if need to interchange the zeta first and the zeta
40859 // last (to ensure the same order in zeta values in any two
40860 // processors)
40861 if (local_zeta_first > local_zeta_last)
40862 {
40863 const double tmp_zeta = local_zeta_first;
40864 local_zeta_first = local_zeta_last;
40865 local_zeta_last = tmp_zeta;
40866 }
40867
40868 const double x1 = vector_bnd_vertices[i][1];
40869 const double y1 = vector_bnd_vertices[i][2];
40870 const double x2 = vector_bnd_vertices[i + 1][1];
40871 const double y2 = vector_bnd_vertices[i + 1][2];
40872 const double local_length =
40873 sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
40874
40875 // Compute the length in zeta units
40876 const double length_side = sqrt(constant_value * area_constraint[i]);
40877 const double length_side_zeta =
40878 (local_length_zeta * length_side) / local_length;
40879
40880 // How many segments should be introduced
40881 const double n_seg_double = length_side_zeta / local_length_zeta;
40882
40883 // One segment initialy (the original one)
40884 unsigned n_seg = 1;
40885
40886 // How many more segments to introduce?
40887 n_seg += static_cast<unsigned>(std::floor(1.0 / n_seg_double));
40888
40889 // Are there segments to introduce? There must be at least one
40890 // segment, the original one
40891 if (n_seg > 0)
40892 {
40893 // The zeta increment
40894 double zeta_increment = (local_length_zeta) / ((double)n_seg);
40895
40896 Vector<double> zeta(1);
40897 // Create the n_seg segmets between each pair of nodes
40898 for (unsigned s = 1; s < n_seg; s++)
40899 {
40900 // Get the coordinates
40901 zeta[0] = local_zeta_first + zeta_increment * double(s);
40902 Vector<double> vertex(2);
40903 mesh_geom_obj_pt->position(zeta, vertex);
40904
40905 // Create the new node
40906 Vector<double> new_node(3);
40907 new_node[0] = zeta[0];
40908 new_node[1] = vertex[0];
40909 new_node[2] = vertex[1];
40910
40911 // Include the new node
40912 new_vector.push_back(new_node);
40913
40914 } // for (s<=n_seg)
40915
40916 } // if (n_seg > 0)
40917
40918 } // if (area_constraint[i] >= 0)
40919
40920 } // for (i < n_vertex-1)
40921
40922 // Once finished all the vertices add the last node to the vector
40923 new_vector.push_back(vector_bnd_vertices[n_vertex - 1]);
40924
40925 // If the new size of the vector (including the added nodes) is
40926 // different from the size of the vector before applying the
40927 // area length constraint then the polyline was updated
40928 n_vertex = new_vector.size();
40929 if (n_vertex != vector_bnd_vertices.size())
40930 {
40931 refinement_applied = true;
40932 }
40933
40934 // Copy the new representation
40935 vector_bnd_vertices.resize(n_vertex);
40936 for (unsigned i = 0; i < n_vertex; i++)
40937 {
40938 vector_bnd_vertices[i].resize(3);
40939 vector_bnd_vertices[i][0] = new_vector[i][0];
40940 vector_bnd_vertices[i][1] = new_vector[i][1];
40941 vector_bnd_vertices[i][2] = new_vector[i][2];
40942 }
40943
40944 } // if (n_vertex > 1)
40945
40946 return refinement_applied;
40947 }
40948
40949 //======================================================================
40950 /// Helper function that performs the unrefinement process
40951 /// on the specified boundary by using the provided vertices
40952 /// representation and the associated target area.
40953 /// NOTE: This is the version that applies unrefinement to shared
40954 /// boundaries
40955 //======================================================================
40956 template<class ELEMENT>
40959 const unsigned& b,
40960 const unsigned& c,
40961 Vector<Vector<double>>& vector_bnd_vertices,
40962 Vector<double>& area_constraint)
40963 {
40964 // Store the vertices not allowed for deletion
40965 std::set<Vector<double>> no_delete_vertex;
40966
40967 // Does the boundary receives connections?
40968 const bool boundary_receive_connections =
40969 this->boundary_connections(b, c, no_delete_vertex);
40970
40971 // Boolean that indicates whether an actual update of the vertex
40972 // coordinates was performed
40973 bool unrefinement_applied = false;
40974
40975 // Return inmedately
40976 if (!Do_shared_boundary_unrefinement_constrained_by_target_areas)
40977 {
40978 return unrefinement_applied;
40979 }
40980
40981 // Strategy to delete nodes:
40982
40983 // Strategy to delete nodes: Consider the target area of the
40984 // elements (e_i and e_(i+1)) sharing the i-th node (middle node),
40985 // if the number of segments to be added is equal to zero for both
40986 // elements then compute the average of both target areas and check
40987 // if the number of segments is still zero, if that holds mark the
40988 // node to be deleted. Before delete the node check whether it is in
40989 // the non_delete_vertex list. Skip the i+1-th node and go for the
40990 // (i+2)-th one, it means, increase the counter for current node by
40991 // two.
40992
40993 // Number of vertices on the boundary
40994 unsigned n_vertex = vector_bnd_vertices.size();
40995
40996 // Compute a constant value
40997 const double constant_value = 4.0 / sqrt(3.0);
40998
40999 if (n_vertex > 2)
41000 {
41001 // Go through all the vertices and delete points when the target
41002 // area indicates zero points along the boundary
41003 for (unsigned i = 1; i < n_vertex - 1; i += 2)
41004 {
41005 // Is a target area assigned to the left and right element of
41006 // the i-th node
41007 if (area_constraint[i - 1] > 0 && area_constraint[i] > 0)
41008 {
41009 // Get the vertices to the left
41010 const double x1 = vector_bnd_vertices[i - 1][0];
41011 const double y1 = vector_bnd_vertices[i - 1][1];
41012 // ... and to the right of the i-th vertex
41013 const double x2 = vector_bnd_vertices[i + 1][0];
41014 const double y2 = vector_bnd_vertices[i + 1][1];
41015
41016 // The distance
41017 const double local_length =
41018 sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
41019
41020 // Get the middle vertex
41021 const double x_m = vector_bnd_vertices[i][0];
41022 const double y_m = vector_bnd_vertices[i][1];
41023
41024 // The average area
41025 const double average_area_constraint =
41026 (area_constraint[i - 1] + area_constraint[i]) / 2.0;
41027
41028 // Compute the base length of the triangle with
41029 // area_constraint area
41030 const double length_side =
41031 sqrt(constant_value * average_area_constraint);
41032
41033 // Is the new length greater than the old one
41034 if ((length_side / local_length) > 1.0)
41035 {
41036 bool do_it = true;
41037
41038 // If the vertex was proposed for deletion check that it is
41039 // allowed for being deleted
41040 if (do_it && boundary_receive_connections)
41041 {
41042 // Is the vertex one of the non deletable vertices
41043 for (std::set<Vector<double>>::iterator it =
41044 no_delete_vertex.begin();
41045 it != no_delete_vertex.end();
41046 it++)
41047 {
41048 // Compute the distance between the proposed node to delete
41049 // and the ones that should not be deleted
41050 const double x = (*it)[0];
41051 const double y = (*it)[1];
41052 double error = (x_m - x) * (x_m - x) + (y_m - y) * (y_m - y);
41053 error = sqrt(error);
41054
41055 if (error <
41057 {
41058 // Do not delete the vertex
41059 do_it = false;
41060 break;
41061 }
41062 }
41063
41064 } // if (do_it && boundary_receive_connections)
41065
41066 // Remove node?
41067 if (do_it)
41068 {
41069 vector_bnd_vertices[i].resize(0);
41070 }
41071 } // if ((local_length / length_side) <= 1.3)
41072
41073 } // if (area_constraint[i] >= 0)
41074
41075 } // for (i < n_vertex-1)
41076
41077 // Create a new (temporary) vector for the nodes, so that deleted nodes
41078 // are not stored
41079 Vector<Vector<double>> compact_vector;
41080
41081 // Compact vector for target areas too
41082 Vector<double> compact_area_constraint;
41083
41084 // Copy only the non deleted nodes
41085 for (unsigned i = 0; i < n_vertex; i++)
41086 {
41087 // If the entry was not deleted include it in the new vector
41088 if (vector_bnd_vertices[i].size() != 0)
41089 {
41090 compact_vector.push_back(vector_bnd_vertices[i]);
41091 }
41092 }
41093
41094 // ------------------------------------------------------------------
41095 // The number of target areas
41096 unsigned n_area_constraint = area_constraint.size();
41097 if (n_area_constraint == 1)
41098 {
41099 // No node could be deleted then just copy the target area
41100 compact_area_constraint.push_back(area_constraint[0]);
41101 }
41102
41103 // Copy the target areas
41104 for (unsigned i = 1; i < n_vertex; i += 2)
41105 {
41106 // If the entry was not deleted include the target areas of both
41107 // elements sharing the node
41108 if (vector_bnd_vertices[i].size() != 0)
41109 {
41110 compact_area_constraint.push_back(area_constraint[i - 1]);
41111 // To catch the case when working with even number of vertices
41112 if (i < n_area_constraint)
41113 {
41114 compact_area_constraint.push_back(area_constraint[i]);
41115 }
41116 }
41117 else
41118 {
41119 // If the node was deleted then compute the new target area as the
41120 // average of the target area of the elements sharing the node
41121 const double new_area_constraint =
41122 (area_constraint[i - 1] + area_constraint[i]) / 2.0;
41123 compact_area_constraint.push_back(new_area_constraint);
41124 }
41125 } // for (i < n_vertex)
41126
41127 // If the size of the compact vector is different from the size of
41128 // the vector before applying the area length constraint then the
41129 // polyline was updated
41130 if (n_vertex != compact_vector.size())
41131 {
41132 unrefinement_applied = true;
41133 }
41134
41135 // Copy back to the original vector
41136 n_vertex = compact_vector.size();
41137 vector_bnd_vertices.resize(n_vertex);
41138 for (unsigned i = 0; i < n_vertex; i++)
41139 {
41140 vector_bnd_vertices[i].resize(2);
41141 vector_bnd_vertices[i][0] = compact_vector[i][0];
41142 vector_bnd_vertices[i][1] = compact_vector[i][1];
41143 }
41144
41145 // Copy back to the original vector of target areas
41146 unsigned ntarget_areas = compact_area_constraint.size();
41147 area_constraint.resize(ntarget_areas);
41148 for (unsigned i = 0; i < ntarget_areas; i++)
41149 {
41150 area_constraint[i] = compact_area_constraint[i];
41151 }
41152
41153 } // if (n_vertex > 2)
41154
41155 return unrefinement_applied;
41156 }
41157
41158 //======================================================================
41159 /// Helper function that performs the refinement process
41160 /// on the specified boundary by using the provided vertices
41161 /// representation and the associated elements target area.
41162 /// NOTE: This is the version that applies refinement to shared
41163 /// boundaries
41164 //======================================================================
41165 template<class ELEMENT>
41168 Vector<Vector<double>>& vector_bnd_vertices,
41169 Vector<double>& area_constraint)
41170 {
41171 // Boolean that indicates whether an actual update of the vertex
41172 // coordinates was performed
41173 bool refinement_applied = false;
41174
41175 // Return inmedately
41176 if (!Do_shared_boundary_refinement_constrained_by_target_areas)
41177 {
41178 return refinement_applied;
41179 }
41180
41181 // Get the number of segments
41182 unsigned nsegments = vector_bnd_vertices.size() - 1;
41183
41184 // Create a new (temporary) vector for the nodes, so that new nodes
41185 // can be stored
41186 Vector<Vector<double>> tmp_bnd_vertices;
41187
41188 // Compute a constant value
41189 const double constant_value = 4.0 / sqrt(3.0);
41190
41191 for (unsigned s = 0; s < nsegments; s++)
41192 {
41193 Vector<double> left_vertex = vector_bnd_vertices[s];
41194 Vector<double> right_vertex = vector_bnd_vertices[s + 1];
41195
41196 // Initial and final point of the segment
41197 const double x1 = left_vertex[0];
41198 const double y1 = left_vertex[1];
41199 const double x2 = right_vertex[0];
41200 const double y2 = right_vertex[1];
41201
41202 // Lenght of the segment
41203 const double segment_length =
41204 sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
41205
41206 // Compute the distance for the new segments
41207 const double new_segment_length =
41208 sqrt(constant_value * area_constraint[s]);
41209
41210 // How many segments should be introduced
41211 const double n_seg_double = new_segment_length / segment_length;
41212
41213 // One segment initialy (the original one)
41214 unsigned nseg = 1;
41215 // How many more segments to introduce?
41216 nseg += static_cast<unsigned>(std::floor(1.0 / n_seg_double));
41217
41218 // The left vertex must be always included, even though no new vertex
41219 // be added
41220 tmp_bnd_vertices.push_back(left_vertex);
41221
41222 // Are there segments to introduce? There must be at least one
41223 // segment, the original one
41224 if (nseg > 0)
41225 {
41226 // Create intermediate vertices
41227 double incrementx = (right_vertex[0] - left_vertex[0]) / (double)(nseg);
41228 double incrementy = (right_vertex[1] - left_vertex[1]) / (double)(nseg);
41229 for (unsigned i = 1; i < nseg; i++)
41230 {
41231 Vector<double> tmp_vertex(2);
41232 tmp_vertex[0] = left_vertex[0] + incrementx * i;
41233 tmp_vertex[1] = left_vertex[1] + incrementy * i;
41234 tmp_bnd_vertices.push_back(tmp_vertex);
41235 } // for (i < nseg)
41236
41237 } // if (nseg > 0)
41238
41239 } // for (s < nsegments)
41240
41241 // Add the last vertex
41242 tmp_bnd_vertices.push_back(vector_bnd_vertices[nsegments]);
41243
41244 // If the new size of the vector (including the added nodes) is
41245 // different from the size of the vector before applying the
41246 // refinement then the polyline was updated
41247 nsegments = tmp_bnd_vertices.size() - 1;
41248 if (nsegments != vector_bnd_vertices.size() - 1)
41249 {
41250 refinement_applied = true;
41251
41252 // Copy across
41253 vector_bnd_vertices.resize(nsegments + 1);
41254 for (unsigned i = 0; i < nsegments + 1; i++)
41255 {
41256 vector_bnd_vertices[i].resize(2);
41257 vector_bnd_vertices[i][0] = tmp_bnd_vertices[i][0];
41258 vector_bnd_vertices[i][1] = tmp_bnd_vertices[i][1];
41259 }
41260 }
41261
41262 return refinement_applied;
41263 }
41264
41265 //======================================================================
41266 /// Updates the polylines representation after restart
41267 //======================================================================
41268 template<class ELEMENT>
41270 TriangleMeshPolygon*& polygon_pt)
41271 {
41272 // **********************************************************************
41273 // 1) Collect the elements adjacet to the polyline boundary id and
41274 // update the polyline
41275 // **********************************************************************
41276
41277 // (1.1) Get the face mesh representation
41278 Vector<Mesh*> face_mesh_pt;
41279 get_face_mesh_representation(polygon_pt, face_mesh_pt);
41280
41281 // (1.2) Create vertices of the polylines by using the vertices of the
41282 // FaceElements
41283 Vector<double> vertex_coord(3); // zeta,x,y
41284 Vector<double> bound_left(1);
41285 Vector<double> bound_right(1);
41286
41287 const unsigned n_polyline = polygon_pt->npolyline();
41288
41289 // Go for each polyline
41290 for (unsigned p = 0; p < n_polyline; p++)
41291 {
41292 // Get the MeshAsGeomObject representation just once per polyline,
41293 // this object is only used by the
41294 // refine_boundary_constrained_by_target_area() method. We get it here
41295 // to ensure that all processors (in a distributed context) get this
41296 // representation just once, and because an AllToAll MPI communication
41297 // is used in this calling
41298 MeshAsGeomObject* mesh_geom_obj_pt =
41299 new MeshAsGeomObject(face_mesh_pt[p]);
41300
41301 // Set of coordinates that are on the boundary
41302 // Set entries are ordered on first entry in vector which stores
41303 // the boundary coordinate so the vertices come out in order!
41304 std::set<Vector<double>> vertex_nodes;
41305
41306 // Vector to store the vertices, transfer the sorted vertices from the
41307 // set to this vector, --- including the z-value ---
41308 Vector<Vector<double>> tmp_vector_vertex_node;
41309
41310 // Vector to store the coordinates of the polylines, same as the
41311 // tmp_vector_vertex_node vector (after adding more nodes) but
41312 // --- without the z-value ---, used to re-generate the polylines
41313 Vector<Vector<double>> vector_vertex_node;
41314
41315#ifdef OOMPH_HAS_MPI
41316 // --------- Stuff to deal with splitted boundaries ---------- Begin -----
41317 // Set of coordinates that are on the boundary (splitted boundary version)
41318 // The first vector is used to allocate the points for each sub-boundary
41319 // Set entries are ordered on first entry in vector which stores
41320 // the boundary coordinate so the vertices come out in order!
41321 Vector<std::set<Vector<double>>> sub_vertex_nodes;
41322
41323 // Vector to store the vertices, transfer the sorted vertices from the
41324 // set (sub_vertex_nodes) to this vector, --- including the z-value ---
41325 Vector<Vector<Vector<double>>> sub_tmp_vector_vertex_node;
41326
41327 // Vector to store the coordinates of the polylines that will represent
41328 // the splitted boundary. Used to pass the info. from sub_vertex_nodes
41329 // but --- without the z-value ---, used to generate the sub-polylines
41330 Vector<Vector<Vector<double>>> sub_vector_vertex_node;
41331 // --------- Stuff to deal with splitted boundaries ----------- End ------
41332#endif
41333
41334 // Get the boundary id
41335 unsigned bound = polygon_pt->curve_section_pt(p)->boundary_id();
41336
41337 /// Use a vector of vector for vertices and target areas to
41338 /// deal with the cases when the boundaries are split by the
41339 /// distribution process
41340
41341 // Loop over the face elements (ordered) and add their vertices
41342 const unsigned nface_element = face_mesh_pt[p]->nelement();
41343
41344 // Store the non halo face elements, the ones from which we will
41345 // get the vertices
41346 Vector<FiniteElement*> non_halo_face_element_pt;
41347 // Map to store the index of the face element on a boundary
41348 std::map<FiniteElement*, unsigned> face_element_index_on_boundary;
41349
41350 for (unsigned ef = 0; ef < nface_element; ++ef)
41351 {
41352 FiniteElement* ele_face_pt = face_mesh_pt[p]->finite_element_pt(ef);
41353 // Skip the halo elements
41354#ifdef OOMPH_HAS_MPI
41355 if (this->is_mesh_distributed())
41356 {
41357 // Only work with non-halo elements
41358 if (ele_face_pt->is_halo())
41359 {
41360 continue;
41361 }
41362 }
41363#endif
41364 // Add the face element to the vector
41365 non_halo_face_element_pt.push_back(ele_face_pt);
41366 face_element_index_on_boundary[ele_face_pt] = ef;
41367 }
41368
41369 // Get the number of non halo face element
41370 const unsigned nnon_halo_face_element = non_halo_face_element_pt.size();
41371
41372 // Map to know the already sorted face elements
41373 std::map<FiniteElement*, bool> face_element_done;
41374
41375 // Number of done face elements
41376 unsigned nsorted_face_elements = 0;
41377
41378#ifdef OOMPH_HAS_MPI
41379 // Counter for sub_boundaries
41380 unsigned nsub_boundaries = 0;
41381#endif // #ifdef OOMPH_HAS_MPI
41382
41383 // Continue until all the face elements have been sorted
41384 // This while is to deal with the cases of splitted boundaries
41385 while (nsorted_face_elements < nnon_halo_face_element)
41386 {
41387 // Get and initial face element
41388 FiniteElement* ele_face_pt = 0;
41389#ifdef PARANOID
41390 bool found_initial_face_element = false;
41391#endif
41392
41393 unsigned iface = 0;
41394 for (iface = 0; iface < nnon_halo_face_element; iface++)
41395 {
41396 ele_face_pt = non_halo_face_element_pt[iface];
41397 // If not done then take it as initial face element
41398 if (!face_element_done[ele_face_pt])
41399 {
41400#ifdef PARANOID
41401 found_initial_face_element = true;
41402#endif
41403 nsorted_face_elements++;
41404 iface++;
41405 break;
41406 }
41407 }
41408
41409#ifdef PARANOID
41410 if (!found_initial_face_element)
41411 {
41412 std::ostringstream error_message;
41413 error_message << "Could not find an initial face element for the "
41414 "current segment\n";
41415 // << "----- Possible memory leak -----\n";
41416 throw OomphLibError(
41417 error_message.str(),
41418 "RefineableTriangleMesh::update_polygon_after_restart()",
41419 OOMPH_EXCEPTION_LOCATION);
41420 }
41421#endif
41422
41423 // Local set of coordinates that are on the boundary
41424 // Set entries are ordered on first entry in vector which stores
41425 // the boundary coordinate so the vertices come out in order!
41426 std::set<Vector<double>> local_vertex_nodes;
41427
41428 // Vector to store the vertices, transfer the sorted vertices from the
41429 // set (local) to this vector (local), --- including the z-value ---
41430 Vector<Vector<double>> local_tmp_vector_vertex_node;
41431
41432 // ------------------------------------------------------------------
41433 // ------------------------------------------------------------------
41434 // -----------------------------------------------------------------
41435 // Add the vertices of the initial face element to the set of local
41436 // sorted vertices
41437 // -----------------------------------------------------------------
41438 unsigned nnode = ele_face_pt->nnode();
41439 // Add the left-hand node to the set:
41440 // Boundary coordinate
41441 ele_face_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
41442 vertex_coord[0] = bound_left[0];
41443
41444 // Actual coordinates
41445 for (unsigned i = 0; i < 2; i++)
41446 {
41447 vertex_coord[i + 1] = ele_face_pt->node_pt(0)->x(i);
41448 }
41449 local_vertex_nodes.insert(vertex_coord);
41450
41451 // Add the right-hand nodes to the set:
41452 // Boundary coordinate
41453 ele_face_pt->node_pt(nnode - 1)->get_coordinates_on_boundary(
41454 bound, bound_right);
41455 vertex_coord[0] = bound_right[0];
41456
41457 // Actual coordinates
41458 for (unsigned i = 0; i < 2; i++)
41459 {
41460 vertex_coord[i + 1] = ele_face_pt->node_pt(nnode - 1)->x(i);
41461 }
41462 local_vertex_nodes.insert(vertex_coord);
41463
41464 // The initial and final node on the set
41465 Node* first_node_pt = ele_face_pt->node_pt(0);
41466 Node* last_node_pt = ele_face_pt->node_pt(nnode - 1);
41467
41468 // Mark the current face element as done
41469 face_element_done[ele_face_pt] = true;
41470
41471 // ------------------------------------------------------------------
41472 // ------------------------------------------------------------------
41473 // ------------------------------------------------------------------
41474
41475 // Continue iterating if a new face element has been added to the
41476 // list
41477 bool face_element_added = false;
41478
41479 // While a new face element has been added to the set of sorted
41480 // face elements then re-iterate
41481 do
41482 {
41483 // Start from the next face elements since we have already added
41484 // the previous one as the initial face element (any previous face
41485 // element had to be added on previous iterations)
41486 for (unsigned iiface = iface; iiface < nnon_halo_face_element;
41487 iiface++)
41488 {
41489 face_element_added = false;
41490 ele_face_pt = non_halo_face_element_pt[iiface];
41491 if (!face_element_done[ele_face_pt])
41492 {
41493 // Get each individual node to check if they are contiguous
41494 nnode = ele_face_pt->nnode();
41495 Node* left_node_pt = ele_face_pt->node_pt(0);
41496 Node* right_node_pt = ele_face_pt->node_pt(nnode - 1);
41497
41498 if (left_node_pt == first_node_pt)
41499 {
41500 first_node_pt = right_node_pt;
41501 face_element_added = true;
41502 }
41503 else if (left_node_pt == last_node_pt)
41504 {
41505 last_node_pt = right_node_pt;
41506 face_element_added = true;
41507 }
41508 else if (right_node_pt == first_node_pt)
41509 {
41510 first_node_pt = left_node_pt;
41511 face_element_added = true;
41512 }
41513 else if (right_node_pt == last_node_pt)
41514 {
41515 last_node_pt = left_node_pt;
41516 face_element_added = true;
41517 }
41518
41519 if (face_element_added)
41520 {
41521 // Add the left-hand node to the set:
41522 // Boundary coordinate
41523 left_node_pt->get_coordinates_on_boundary(bound, bound_left);
41524 vertex_coord[0] = bound_left[0];
41525
41526 // Actual coordinates
41527 for (unsigned i = 0; i < 2; i++)
41528 {
41529 vertex_coord[i + 1] = left_node_pt->x(i);
41530 }
41531 local_vertex_nodes.insert(vertex_coord);
41532
41533 // Add the right-hand nodes to the set:
41534 // Boundary coordinate
41535 right_node_pt->get_coordinates_on_boundary(bound, bound_right);
41536 vertex_coord[0] = bound_right[0];
41537
41538 // Actual coordinates
41539 for (unsigned i = 0; i < 2; i++)
41540 {
41541 vertex_coord[i + 1] = right_node_pt->x(i);
41542 }
41543 local_vertex_nodes.insert(vertex_coord);
41544
41545 // Mark as done only if one of its nodes has been
41546 // added to the list
41547 face_element_done[ele_face_pt] = true;
41548 nsorted_face_elements++;
41549
41550 break;
41551 }
41552
41553 } // if (!edge_done[edge])
41554 } // for (iiedge < nedges)
41555 } while (face_element_added &&
41556 (nsorted_face_elements < nnon_halo_face_element));
41557
41558 // -----------------------------------------------------------------
41559 // At this point we already have a sorted set of nodes and
41560 // can be used to peform the unrefinement and refinement procedures
41561 // -----------------------------------------------------------------
41562
41563 // Get the number of nodes on the list
41564 const unsigned nlocal_nodes = local_vertex_nodes.size();
41565 // Change representation to vector for easy of handling ...
41566 local_tmp_vector_vertex_node.resize(nlocal_nodes);
41567
41568 // Copy the vertices of the nodes
41569 unsigned counter = 0;
41570 std::set<Vector<double>>::iterator it_vertex;
41571 for (it_vertex = local_vertex_nodes.begin();
41572 it_vertex != local_vertex_nodes.end();
41573 it_vertex++)
41574 {
41575 local_tmp_vector_vertex_node[counter].resize(3);
41576 local_tmp_vector_vertex_node[counter][0] = (*it_vertex)[0];
41577 local_tmp_vector_vertex_node[counter][1] = (*it_vertex)[1];
41578 local_tmp_vector_vertex_node[counter][2] = (*it_vertex)[2];
41579 counter++;
41580 }
41581
41582 // *********************************************************************
41583 // 3) Create the vertices along the boundary using the target area to
41584 // define the distance among them
41585 // *********************************************************************
41586
41587 // Clear the local containter to recover the nodes ordered using the
41588 // zeta value
41589 local_vertex_nodes.clear();
41590
41591 // At the end of each unrefinement/refinement step store the new nodes
41592 // on the set that will give rise to the vertices of the new polyline
41593 // representation
41594 unsigned nnew_nodes = local_tmp_vector_vertex_node.size();
41595 for (unsigned i = 0; i < nnew_nodes; i++)
41596 {
41597 vertex_coord[0] = local_tmp_vector_vertex_node[i][0];
41598 vertex_coord[1] = local_tmp_vector_vertex_node[i][1];
41599 vertex_coord[2] = local_tmp_vector_vertex_node[i][2];
41600 vertex_nodes.insert(vertex_coord); // Global container
41601 local_vertex_nodes.insert(vertex_coord);
41602 }
41603
41604#ifdef OOMPH_HAS_MPI
41605 if (this->is_mesh_distributed())
41606 {
41607 // Add the set of vertices for the boundary, this will help to detect
41608 // if we need to deal with sub_boundaries and sub_polylines represen.
41609 sub_vertex_nodes.push_back(local_vertex_nodes);
41610 // Increase the counter for sub_boundaries
41611 nsub_boundaries++;
41612 }
41613#endif
41614
41615 } // while(nsorted_face_elements < nnon_halo_face_element)
41616
41617 // Now turn into vector for ease of handling...
41618 unsigned npoly_vertex = vertex_nodes.size();
41619 tmp_vector_vertex_node.resize(npoly_vertex);
41620 unsigned count = 0;
41621 std::set<Vector<double>>::iterator it;
41622 for (it = vertex_nodes.begin(); it != vertex_nodes.end(); ++it)
41623 {
41624 tmp_vector_vertex_node[count].resize(3);
41625 tmp_vector_vertex_node[count][0] = (*it)[0];
41626 tmp_vector_vertex_node[count][1] = (*it)[1];
41627 tmp_vector_vertex_node[count][2] = (*it)[2];
41628 ++count;
41629 }
41630
41631#ifdef OOMPH_HAS_MPI
41632 // --------- Stuff for the sub_boundaries ----- Begin section ---------
41633#ifdef PARANOID
41634 unsigned nsub_boundaries_set = sub_vertex_nodes.size();
41635 if (nsub_boundaries_set != nsub_boundaries)
41636 {
41637 std::ostringstream error_message;
41638 error_message
41639 << "The number of found sub-boundaries and the number of counted\n"
41640 << "sub-boundaries are different:\n"
41641 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
41642 << "Number of counted sub-boundaries: (" << nsub_boundaries << ")\n";
41643 throw OomphLibError(
41644 error_message.str(),
41645 "RefineableTriangleMesh::update_polygon_after_restart()",
41646 OOMPH_EXCEPTION_LOCATION);
41647 }
41648#endif
41649
41650 // Verify if need to deal with sub_boundaries
41651 if (this->is_mesh_distributed() && nsub_boundaries > 1)
41652 {
41653 // Mark the boundary as been splitted in the partition process
41654 this->Boundary_was_splitted[bound] = true;
41655 // Resize the vector to store the info. of sub-boundaries
41656 sub_tmp_vector_vertex_node.resize(nsub_boundaries);
41657 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
41658 {
41659 // Turn info. into vector for ease of handling...
41660 const unsigned nsubpoly_vertex = sub_vertex_nodes[isub].size();
41661 sub_tmp_vector_vertex_node[isub].resize(nsubpoly_vertex);
41662 unsigned subcount = 0;
41663 std::set<Vector<double>>::iterator subit;
41664 for (subit = sub_vertex_nodes[isub].begin();
41665 subit != sub_vertex_nodes[isub].end();
41666 ++subit)
41667 {
41668 sub_tmp_vector_vertex_node[isub][subcount].resize(3);
41669 sub_tmp_vector_vertex_node[isub][subcount][0] = (*subit)[0];
41670 sub_tmp_vector_vertex_node[isub][subcount][1] = (*subit)[1];
41671 sub_tmp_vector_vertex_node[isub][subcount][2] = (*subit)[2];
41672 ++subcount;
41673 }
41674 }
41675 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
41676 // --------- Stuff for the sub_boundaries ----- End section ------------
41677#endif // OOMPH_HAS_MPI
41678
41679
41680 // For further processing the three-dimensional vector
41681 // has to be reduced to a two-dimensional vector
41682 unsigned n_vertex = tmp_vector_vertex_node.size();
41683
41684 // Resize the vector for vectices
41685 vector_vertex_node.resize(n_vertex);
41686 for (unsigned i = 0; i < n_vertex; i++)
41687 {
41688 vector_vertex_node[i].resize(2);
41689 vector_vertex_node[i][0] = tmp_vector_vertex_node[i][1];
41690 vector_vertex_node[i][1] = tmp_vector_vertex_node[i][2];
41691 }
41692
41693#ifdef OOMPH_HAS_MPI
41694 // --------- Stuff for the sub_boundaries ----- Begin section ----------
41695 // Verify if need to deal with sub_boundaries
41696 if (this->is_mesh_distributed() && nsub_boundaries > 1)
41697 {
41698 // For further processing the three-dimensional vector
41699 // has to be reduced to a two-dimensional vector
41700 // Resize the vector to store the info. of sub-boundaries
41701 sub_vector_vertex_node.resize(nsub_boundaries);
41702 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
41703 {
41704 const unsigned subn_vertex = sub_tmp_vector_vertex_node[isub].size();
41705 // Resize the vector for vectices
41706 sub_vector_vertex_node[isub].resize(subn_vertex);
41707 for (unsigned i = 0; i < subn_vertex; i++)
41708 {
41709 sub_vector_vertex_node[isub][i].resize(2);
41710 sub_vector_vertex_node[isub][i][0] =
41711 sub_tmp_vector_vertex_node[isub][i][1];
41712 sub_vector_vertex_node[isub][i][1] =
41713 sub_tmp_vector_vertex_node[isub][i][2];
41714 }
41715 }
41716 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
41717
41718 // We already have the info. for the sub-boundaries (if necessary)
41719 // and then we can create the sub-boundaries representations to
41720 // ease the generation of the mesh by Triangle
41721
41722 // --------- Stuff for the sub_boundaries ----- End section ------------
41723#endif // OOMPH_HAS_MPI
41724
41725 // *********************************************************************
41726 // 4) Check for contiguousness
41727 // *********************************************************************
41728#ifdef OOMPH_HAS_MPI
41729 // Only perform this checking if the mesh is not distributed
41730 // When the mesh is distributed the polylines continuity is
41731 // addressed with the sort_polylines_helper() method
41732 if (!this->is_mesh_distributed())
41733#endif
41734 {
41735 if (p > 0)
41736 {
41737 // Final end point of previous line
41738 Vector<double> final_vertex_of_previous_segment;
41739 unsigned n_prev_vertex =
41740 polygon_pt->curve_section_pt(p - 1)->nvertex();
41741 final_vertex_of_previous_segment =
41742 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(n_prev_vertex -
41743 1);
41744
41745 unsigned prev_seg_boundary_id =
41746 polygon_pt->curve_section_pt(p - 1)->boundary_id();
41747
41748 // Find the error between the final vertex of the previous
41749 // line and the first vertex of the current line
41750 double error = 0.0;
41751 for (unsigned i = 0; i < 2; i++)
41752 {
41753 const double dist = final_vertex_of_previous_segment[i] -
41754 (*vector_vertex_node.begin())[i];
41755 error += dist * dist;
41756 }
41757 error = sqrt(error);
41758
41759 // If the error is bigger than the tolerance then
41760 // we probably need to reverse, but better check
41762 {
41763 // Find the error between the final vertex of the previous
41764 // line and the last vertex of the current line
41765 double rev_error = 0.0;
41766 for (unsigned i = 0; i < 2; i++)
41767 {
41768 const double dist = final_vertex_of_previous_segment[i] -
41769 (*--vector_vertex_node.end())[i];
41770 rev_error += dist * dist;
41771 }
41772 rev_error = sqrt(rev_error);
41773
41774 if (rev_error >
41776 {
41777 // It could be possible that the first segment be reversed and we
41778 // did not notice it because this check does not apply for the
41779 // first segment. We can verify if the first segment is reversed
41780 // by using the vertex number 1
41781 if (p == 1)
41782 {
41783 // Initial end point of previous line
41784 Vector<double> initial_vertex_of_previous_segment;
41785
41786 initial_vertex_of_previous_segment =
41787 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(0);
41788
41789 unsigned prev_seg_boundary_id =
41790 polygon_pt->curve_section_pt(p - 1)->boundary_id();
41791
41792 // Find the error between the initial vertex of the previous
41793 // line and the first vertex of the current line
41794 double error = 0.0;
41795 for (unsigned i = 0; i < 2; i++)
41796 {
41797 const double dist = initial_vertex_of_previous_segment[i] -
41798 (*vector_vertex_node.begin())[i];
41799 error += dist * dist;
41800 }
41801 error = sqrt(error); // Reversed only the previous one
41802
41803 // If the error is bigger than the tolerance then
41804 // we probably need to reverse, but better check
41805 if (error >
41807 {
41808 // Find the error between the final vertex of the previous
41809 // line and the last vertex of the current line
41810 double rev_error = 0.0;
41811 for (unsigned i = 0; i < 2; i++)
41812 {
41813 const double dist = initial_vertex_of_previous_segment[i] -
41814 (*--vector_vertex_node.end())[i];
41815 rev_error += dist * dist;
41816 }
41817 rev_error =
41818 sqrt(rev_error); // Reversed both the current one and
41819 // the previous one
41820
41821 if (rev_error >
41823 {
41824 std::ostringstream error_stream;
41825 error_stream
41826 << "The distance between the first node of the current\n"
41827 << "line segment (boundary " << bound
41828 << ") and either end of "
41829 << "the previous line segment\n"
41830 << "(boundary " << prev_seg_boundary_id
41831 << ") is bigger than "
41832 << "the desired tolerance "
41834 << ".\n"
41835 << "This suggests that the polylines defining the "
41836 "polygonal\n"
41837 << "representation are not properly ordered.\n"
41838 << "Fail on last vertex of polyline: ("
41839 << prev_seg_boundary_id
41840 << ") and\nfirst vertex of polyline (" << bound
41841 << ").\nThis should have failed when first trying to"
41842 << " construct the\npolygon.\n";
41843 throw OomphLibError(
41844 error_stream.str(),
41845 "RefineableTriangleMesh::update_polygon_after_restart()",
41846 OOMPH_EXCEPTION_LOCATION);
41847 }
41848 else
41849 {
41850 // Reverse both
41851 // Reverse the current vector to line up with the previous
41852 // one
41853 std::reverse(vector_vertex_node.begin(),
41854 vector_vertex_node.end());
41855 polygon_pt->polyline_pt(p - 1)->reverse();
41856 }
41857 }
41858 else
41859 {
41860 // Reverse the previous one
41861 polygon_pt->polyline_pt(p - 1)->reverse();
41862 }
41863
41864 } // if p == 1
41865 else
41866 {
41867 std::ostringstream error_stream;
41868 error_stream
41869 << "The distance between the first node of the current\n"
41870 << "line segment (boundary " << bound
41871 << ") and either end of "
41872 << "the previous line segment\n"
41873 << "(boundary " << prev_seg_boundary_id
41874 << ") is bigger than the "
41875 << "desired tolerance "
41877 << ".\n"
41878 << "This suggests that the polylines defining the polygonal\n"
41879 << "representation are not properly ordered.\n"
41880 << "Fail on last vertex of polyline: ("
41881 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
41882 << bound << ").\n"
41883 << "This should have failed when first trying to construct "
41884 "the\n"
41885 << "polygon.\n";
41886 throw OomphLibError(
41887 error_stream.str(),
41888 "RefineableTriangleMesh::update_polygon_after_restart()",
41889 OOMPH_EXCEPTION_LOCATION);
41890 }
41891 }
41892 else
41893 {
41894 // Reverse the current vector to line up with the previous one
41895 std::reverse(vector_vertex_node.begin(),
41896 vector_vertex_node.end());
41897 }
41898 } // error
41899 } // p > 0
41900 } // is mesh not distributed
41901
41902 // *********************************************************************
41903 // 5) Update the polylines representation
41904 // *********************************************************************
41905 // if (applied_area_length_constraint)
41906 // If only applied when there is a change then it keeps the
41907 // previous polyline representation, it means, it does not delete
41908 // the boundaries that are not part of the domain. We must update
41909 // the boundary representation
41910 {
41911 n_vertex = vector_vertex_node.size();
41912
41913 // Now update the polyline according to the new vertices
41914 // The new one representation
41915 TriangleMeshPolyLine* tmp_polyline_pt =
41916 new TriangleMeshPolyLine(vector_vertex_node, bound);
41917
41918 // for (unsigned h = 0; h < vector_vertex_node.size(); h++)
41919 // {
41920 // DEBP(h);
41921 // DEBP(vector_vertex_node[h][0]);
41922 // DEBP(vector_vertex_node[h][1]);
41923 // }
41924
41925 // Create a temporal "curve section" version of the recently created
41926 // polyline
41927 TriangleMeshCurveSection* tmp_curve_section_pt = tmp_polyline_pt;
41928
41929 // Tolerance below which the middle point can be deleted
41930 // (ratio of deflection to element length)
41931 double unrefinement_tolerance =
41932 polygon_pt->polyline_pt(p)->unrefinement_tolerance();
41933
41934 // Tolerance to add points
41935 double refinement_tolerance =
41936 polygon_pt->polyline_pt(p)->refinement_tolerance();
41937
41938 // Establish refinement and unrefinement tolerance
41939 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
41940 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
41941
41942 // Establish the maximum length constraint
41943 double maximum_length = polygon_pt->polyline_pt(p)->maximum_length();
41944 tmp_polyline_pt->set_maximum_length(maximum_length);
41945
41946 if (n_vertex >= 2)
41947 {
41948 // Pass the connection information from the old polyline to the
41949 // new one
41950 this->copy_connection_information(polygon_pt->polyline_pt(p),
41951 tmp_curve_section_pt);
41952 }
41953
41954 // Now update the polyline according to the new vertices but
41955 // first check if the object is allowed to delete the representation
41956 // or if it should be done by other object
41957 bool delete_it_on_destructor = false;
41958
41959 std::set<TriangleMeshCurveSection*>::iterator it =
41960 this->Free_curve_section_pt.find(polygon_pt->curve_section_pt(p));
41961
41962 if (it != this->Free_curve_section_pt.end())
41963 {
41964 this->Free_curve_section_pt.erase(it);
41965 delete polygon_pt->curve_section_pt(p);
41966 delete_it_on_destructor = true;
41967 }
41968
41969 // *****************************************************************
41970 // Copying the new representation
41971 polygon_pt->curve_section_pt(p) = tmp_polyline_pt;
41972
41973 // Update the Boundary - Polyline map
41974 this->Boundary_curve_section_pt[bound] =
41975 polygon_pt->curve_section_pt(p);
41976
41977 if (delete_it_on_destructor)
41978 {
41979 this->Free_curve_section_pt.insert(polygon_pt->curve_section_pt(p));
41980 }
41981
41982#ifdef OOMPH_HAS_MPI
41983 // --------- Stuff for the sub_boundaries ----- Begin section --------
41984 // Verify if need to deal with sub_boundaries
41985 if (this->is_mesh_distributed() && nsub_boundaries > 1)
41986 {
41987 // Create temporary representations for the boundaries, only to
41988 // create the mesh when calling Triangle
41989 // Clear all previous stored data
41990 this->Boundary_subpolylines[bound].clear();
41991 // Now create storage for the sub-boundaries
41992 this->Boundary_subpolylines[bound].resize(nsub_boundaries);
41993 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
41994 {
41995 // Now update the polyline according to the sub set of
41996 // vertices, set the chunk number of the polyline
41997 TriangleMeshPolyLine* sub_tmp_polyline_pt =
41999 sub_vector_vertex_node[isub], bound, isub);
42000
42001 // Add the sub-polyline to the container to represent the boundary
42002 // in parts
42003 this->Boundary_subpolylines[bound][isub] = sub_tmp_polyline_pt;
42004
42005 // No need to send the unrefinement/refinement and maximum
42006 // length constraints since these are only temporary
42007 // representations. These polylines can be deleted once the
42008 // new polygons that represent the distributed domain have
42009 // been created
42010
42011 } // for (isub < nsub_boundaries)
42012
42013 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
42014 // --------- Stuff for the sub_boundaries ----- End section ---------
42015#endif // OOMPH_HAS_MPI
42016
42017 } // update polyline representation
42018
42019 // Delete the allocated memory for the geometric object that
42020 // represents the curvilinear boundary
42021 delete mesh_geom_obj_pt;
42022
42023 } // npolyline
42024
42025 // Cleanup the face mesh
42026 for (unsigned p = 0; p < n_polyline; p++)
42027 {
42028 face_mesh_pt[p]->flush_node_storage();
42029 delete face_mesh_pt[p];
42030 }
42031 }
42032
42033
42034 //======================================================================
42035 /// Updates the open curve representation after restart
42036 //======================================================================
42037 template<class ELEMENT>
42039 TriangleMeshOpenCurve*& open_curve_pt)
42040 {
42041 // **********************************************************************
42042 // 1) Get the vertices along the boundaries ids of the polylines and
42043 // update them
42044 // **********************************************************************
42045
42046 // (1.1) Get the face mesh representation
42047 Vector<Mesh*> face_mesh_pt;
42048 get_face_mesh_representation(open_curve_pt, face_mesh_pt);
42049
42050 // (1.2) Create vertices of the polylines by using the vertices of the
42051 // FaceElements
42052 Vector<double> vertex_coord(3); // zeta,x,y
42053 Vector<double> bound_left(1);
42054 Vector<double> bound_right(1);
42055
42056 const unsigned ncurve_section = open_curve_pt->ncurve_section();
42057 // Go for each curve section
42058 for (unsigned cs = 0; cs < ncurve_section; cs++)
42059 {
42060 // Get the MeshAsGeomObject representation just once per polyline,
42061 // this object is only used by the
42062 // refine_boundary_constrained_by_target_area() method. We get it here
42063 // to ensure that all processors (in a distributed context) get this
42064 // representation just once, and because an AllToAll MPI communication
42065 // is used in this calling
42066 MeshAsGeomObject* mesh_geom_obj_pt =
42067 new MeshAsGeomObject(face_mesh_pt[cs]);
42068
42069 // Get the boundary id
42070 const unsigned bound = open_curve_pt->curve_section_pt(cs)->boundary_id();
42071
42072 /// Use a vector of vector for vertices and target areas to deal
42073 /// with the cases when the boundaries are split bn the
42074 /// distribution process. Internal boundaries may be completely or
42075 /// partially overlapped by shared boundaries
42076
42077 // Loop over the face elements and add their vertices (they are
42078 // automatically sorted because of the set)
42079 const unsigned nface_element = face_mesh_pt[cs]->nelement();
42080 // Store the non halo elements and the element at the other side of
42081 // the boundary (whatever it be halo or not), the first will be the
42082 // ones from which we will get the vertices (in even position)
42083 Vector<FiniteElement*> non_halo_doubled_face_element_pt;
42084
42085 // Map to store the index of the face element on a boundary
42086 std::map<FiniteElement*, unsigned> face_element_index_on_boundary;
42087
42088 // Map to know the already sorted face elements
42089 std::map<FiniteElement*, bool> face_element_done;
42090
42091 for (unsigned ef = 0; ef < nface_element; ++ef)
42092 {
42093 FiniteElement* ele_face_pt = face_mesh_pt[cs]->finite_element_pt(ef);
42094
42095 // Skip the halo elements (not used as base elements, only
42096 // include those elements which one of its counterparts -- at the
42097 // other side of the boundary -- is non halo)
42098#ifdef OOMPH_HAS_MPI
42099 if (this->is_mesh_distributed())
42100 {
42101 // Only work with non-halo elements
42102 if (ele_face_pt->is_halo())
42103 {
42104 continue;
42105 }
42106 }
42107#endif
42108
42109 // Check if not already done
42110 if (!face_element_done[ele_face_pt])
42111 {
42112 // Add the element and look for the element at the other side
42113 // of the boundary to add it immediately after the new added
42114 // element
42115 non_halo_doubled_face_element_pt.push_back(ele_face_pt);
42116 // Create the map of the face element with the index
42117 face_element_index_on_boundary[ele_face_pt] = ef;
42118 // Mark the current element as done
42119 face_element_done[ele_face_pt] = true;
42120 // Get the number of nodes
42121 const unsigned nnodes = ele_face_pt->nnode();
42122 // Get the left and right node to look for the elements at the
42123 // other side of the boundary
42124 Node* left_node_pt = ele_face_pt->node_pt(0);
42125 Node* right_node_pt = ele_face_pt->node_pt(nnodes - 1);
42126
42127#ifdef PARANOID
42128 // Flag to know if the element at the other side of the
42129 // boundary was found
42130 bool found_other_side_face_ele = false;
42131#endif
42132 for (unsigned iface = 0; iface < nface_element; iface++)
42133 {
42134 // Get the candidate face element
42135 FiniteElement* cele_face_pt =
42136 face_mesh_pt[cs]->finite_element_pt(iface);
42137 // Check if not already done
42138 if (!face_element_done[cele_face_pt])
42139 {
42140 Node* cleft_node_pt = cele_face_pt->node_pt(0);
42141 Node* cright_node_pt = cele_face_pt->node_pt(nnodes - 1);
42142
42143 // Check if the nodes are the same
42144 if ((left_node_pt == cleft_node_pt &&
42145 right_node_pt == cright_node_pt) ||
42146 (left_node_pt == cright_node_pt &&
42147 right_node_pt == cleft_node_pt))
42148 {
42149 // Add the element to the storage
42150 non_halo_doubled_face_element_pt.push_back(cele_face_pt);
42151 // ... and mark the element as done
42152 face_element_done[cele_face_pt] = true;
42153 // Create the map of the face element with the index
42154 face_element_index_on_boundary[cele_face_pt] = iface;
42155#ifdef PARANOID
42156 // Set the flag of found other side face element
42157 found_other_side_face_ele = true;
42158#endif
42159 break;
42160 }
42161 }
42162 } // (iface < nface_element)
42163
42164#ifdef PARANOID
42165 if (!found_other_side_face_ele)
42166 {
42167 std::ostringstream error_message;
42168 error_message
42169 << "The face element at the other side of the boundary (" << bound
42170 << ") was not found!!\n"
42171 << "These are the nodes of the face element:\n"
42172 << "(" << left_node_pt->x(0) << ", " << left_node_pt->x(1) << ") "
42173 << "and (" << right_node_pt->x(0) << "," << right_node_pt->x(1)
42174 << ")\n\n";
42175 throw OomphLibError(
42176 error_message.str(),
42177 "RefineableTriangleMesh::update_open_curve_after_restart()",
42178 OOMPH_EXCEPTION_LOCATION);
42179 }
42180#endif
42181 } // if (!face_ele_done[ele_face_pt])
42182
42183 } // (ef < nface_element)
42184
42185 // Clear the map of the already done face elements
42186 // This will now be used to sort the face elements
42187 face_element_done.clear();
42188
42189 // Set of coordinates that are on the boundary
42190 // The entries are sorted on first entry in vector which stores
42191 // the boundary coordinate so the vertices come out in order!
42192 std::set<Vector<double>> vertex_nodes;
42193
42194 // Vector to store the vertices, transfer the sorted vertices from the
42195 // set to this vector, --- including the z-value ---
42196 Vector<Vector<double>> tmp_vector_vertex_node;
42197
42198 // Vector to store the coordinates of the polylines, same as the
42199 // tmp_vector_vertex_node vector (after adding more nodes) but
42200 // --- without the z-value ---, used to re-generate the polylines
42201 Vector<Vector<double>> vector_vertex_node;
42202
42203#ifdef OOMPH_HAS_MPI
42204 // Indicates if the set of vertices give rise to a internal
42205 // boundary that will be used as shared boundary or as normal
42206 // internal boundary -- Only used to deal with internal boundaries
42207 // in a distributed scheme
42208 std::vector<bool> internal_to_shared_boundary;
42209
42210 // --------- Stuff to deal with splitted boundaries ---------- Begin -----
42211 // Set of coordinates that are on the boundary (splitted boundary version)
42212 // The first vector is used to allocate the points for each sub-boundary
42213 // Set entries are ordered on first entry in vector which stores
42214 // the boundary coordinate so the vertices come out in order!
42215 Vector<std::set<Vector<double>>> sub_vertex_nodes;
42216
42217 // Vector to store the vertices, transfer the sorted vertices from the
42218 // set (sub_vertex_nodes) to this vector, --- including the z-value ---
42219 Vector<Vector<Vector<double>>> sub_tmp_vector_vertex_node;
42220
42221 // Vector to store the coordinates of the polylines that will represent
42222 // the splitted boundary. Used to pass the info. from sub_vertex_nodes
42223 // but --- without the z-value ---, used to generate the sub-polylines
42224 Vector<Vector<Vector<double>>> sub_vector_vertex_node;
42225
42226 // --------- Stuff to deal with splitted boundaries ----------- End ------
42227#endif
42228
42229 // Sort face elements, separate those with both nonhalo face
42230 // elements from those with one halo and one nonhalo face element
42231
42232 // Number of done face elements
42233 unsigned nsorted_face_elements = 0;
42234
42235#ifdef OOMPH_HAS_MPI
42236 // Counter for sub_boundaries
42237 unsigned nsub_boundaries = 0;
42238#endif // #ifdef OOMPH_HAS_MPI
42239
42240 // Total number of non halo double face element
42241 const unsigned nnon_halo_doubled_face_ele =
42242 non_halo_doubled_face_element_pt.size();
42243
42244 // Continue until all the face elements have been sorted
42245 // This while is to deal with the cases of splitted boundaries
42246 while (nsorted_face_elements < nnon_halo_doubled_face_ele)
42247 {
42248 // Get and initial face element
42249 FiniteElement* ele_face_pt = 0;
42250 FiniteElement* repeated_ele_face_pt = 0;
42251#ifdef PARANOID
42252 bool found_initial_face_element = false;
42253#endif
42254
42255 // Flag to know if we are working with a face element which the
42256 // face element at the other side of the boundary is also non
42257 // halo
42258 bool both_root_face_elements_are_nonhalo = false;
42259
42260 unsigned iface = 0;
42261 for (iface = 0; iface < nnon_halo_doubled_face_ele; iface += 2)
42262 {
42263 ele_face_pt = non_halo_doubled_face_element_pt[iface];
42264 // If not done then take it as initial face element
42265 if (!face_element_done[ele_face_pt])
42266 {
42267 // Mark it as done
42268 face_element_done[ele_face_pt] = true;
42269 // Get the other side boundary face element
42270 repeated_ele_face_pt = non_halo_doubled_face_element_pt[iface + 1];
42271 // ... also mark as done the repeated face element
42272 face_element_done[repeated_ele_face_pt] = true;
42273
42274#ifdef OOMPH_HAS_MPI
42275 if (!repeated_ele_face_pt->is_halo())
42276 {
42277 both_root_face_elements_are_nonhalo = true;
42278 }
42279#endif // #ifdef OOMPH_HAS_MPI
42280
42281 // Plus two because internal boundaries have
42282 // two face elements per each edge
42283 nsorted_face_elements += 2;
42284 iface += 2;
42285#ifdef PARANOID
42286 // And set the flag to true
42287 found_initial_face_element = true;
42288#endif
42289 break;
42290 }
42291 }
42292
42293#ifdef PARANOID
42294 if (!found_initial_face_element)
42295 {
42296 std::ostringstream error_message;
42297 error_message << "Could not find an initial face element for the "
42298 "current segment\n";
42299 // << "----- Possible memory leak -----\n";
42300 throw OomphLibError(error_message.str(),
42301 OOMPH_CURRENT_FUNCTION,
42302 OOMPH_EXCEPTION_LOCATION);
42303 }
42304#endif
42305
42306 // Local set of coordinates that are on the boundary Set entries
42307 // are ordered on first entry in vector which stores the boundary
42308 // coordinate so the vertices come out in order
42309 std::set<Vector<double>> local_vertex_nodes;
42310
42311 // Vector to store the vertices, transfer the sorted vertices from the
42312 // set (local) to this vector (local), --- including the z-value ---
42313 Vector<Vector<double>> local_tmp_vector_vertex_node;
42314
42315 // ------------------------------------------------------------------
42316 // ------------------------------------------------------------------
42317 // Add the vertices of the initial face element to the set of local
42318 // sorted vertices
42319 // ------------------------------------------------------------------
42320 // ------------------------------------------------------------------
42321 const unsigned nnode = ele_face_pt->nnode();
42322 // Add the left-hand node to the set:
42323 // Boundary coordinate
42324 ele_face_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
42325 vertex_coord[0] = bound_left[0];
42326
42327 // Actual coordinates
42328 for (unsigned i = 0; i < 2; i++)
42329 {
42330 vertex_coord[i + 1] = ele_face_pt->node_pt(0)->x(i);
42331 }
42332 local_vertex_nodes.insert(vertex_coord);
42333
42334 // Add the right-hand node to the set:
42335 // Boundary coordinate
42336 ele_face_pt->node_pt(nnode - 1)->get_coordinates_on_boundary(
42337 bound, bound_right);
42338 vertex_coord[0] = bound_right[0];
42339
42340 // Actual coordinates
42341 for (unsigned i = 0; i < 2; i++)
42342 {
42343 vertex_coord[i + 1] = ele_face_pt->node_pt(nnode - 1)->x(i);
42344 }
42345 local_vertex_nodes.insert(vertex_coord);
42346
42347 // The initial and final node on the set
42348 Node* first_node_pt = ele_face_pt->node_pt(0);
42349 Node* last_node_pt = ele_face_pt->node_pt(nnode - 1);
42350
42351 // Continue iterating if a new face element has been added to the
42352 // list
42353 bool face_element_added = false;
42354
42355 // While a new face element has been added to the set of sorted
42356 // face elements then re-iterate
42357 do
42358 {
42359 // Start from the next face elements since we have already
42360 // added the previous one as the initial face element (any
42361 // previous face element had to be added on previous
42362 // iterations)
42363 for (unsigned iiface = iface; iiface < nnon_halo_doubled_face_ele;
42364 iiface += 2)
42365 {
42366 face_element_added = false;
42367 ele_face_pt = non_halo_doubled_face_element_pt[iiface];
42368
42369 // Check that the face element with which we are working has
42370 // the same conditions as the root face element (both faces
42371 // are nonhalo or one face is halo and the other nonhalo)
42372
42373 // Get the face element at the other side of the boundary
42374 repeated_ele_face_pt = non_halo_doubled_face_element_pt[iiface + 1];
42375 bool both_face_elements_are_nonhalo = false;
42376
42377#ifdef OOMPH_HAS_MPI
42378 if (!repeated_ele_face_pt->is_halo())
42379 {
42380 both_face_elements_are_nonhalo = true;
42381 }
42382#endif // #ifdef OOMPH_HAS_MPI
42383
42384 if (!face_element_done[ele_face_pt] &&
42385 (both_face_elements_are_nonhalo ==
42386 both_root_face_elements_are_nonhalo))
42387 {
42388 // Get each individual node to check if they are contiguous
42389 const unsigned nlnode = ele_face_pt->nnode();
42390 Node* left_node_pt = ele_face_pt->node_pt(0);
42391 Node* right_node_pt = ele_face_pt->node_pt(nlnode - 1);
42392
42393 if (left_node_pt == first_node_pt)
42394 {
42395 first_node_pt = right_node_pt;
42396 face_element_added = true;
42397 }
42398 else if (left_node_pt == last_node_pt)
42399 {
42400 last_node_pt = right_node_pt;
42401 face_element_added = true;
42402 }
42403 else if (right_node_pt == first_node_pt)
42404 {
42405 first_node_pt = left_node_pt;
42406 face_element_added = true;
42407 }
42408 else if (right_node_pt == last_node_pt)
42409 {
42410 last_node_pt = left_node_pt;
42411 face_element_added = true;
42412 }
42413
42414 if (face_element_added)
42415 {
42416 // Add the left-hand node to the set:
42417 // Boundary coordinate
42418 left_node_pt->get_coordinates_on_boundary(bound, bound_left);
42419 vertex_coord[0] = bound_left[0];
42420
42421 // Actual coordinates
42422 for (unsigned i = 0; i < 2; i++)
42423 {
42424 vertex_coord[i + 1] = left_node_pt->x(i);
42425 }
42426 local_vertex_nodes.insert(vertex_coord);
42427
42428 // Add the right-hand nodes to the set:
42429 // Boundary coordinate
42430 right_node_pt->get_coordinates_on_boundary(bound, bound_right);
42431 vertex_coord[0] = bound_right[0];
42432
42433 // Actual coordinates
42434 for (unsigned i = 0; i < 2; i++)
42435 {
42436 vertex_coord[i + 1] = right_node_pt->x(i);
42437 }
42438 local_vertex_nodes.insert(vertex_coord);
42439
42440 // Mark as done only if one of its nodes has been
42441 // added to the list
42442 face_element_done[ele_face_pt] = true;
42443 // .. also mark as done the face element at the othe side of
42444 // the boundary
42445 repeated_ele_face_pt =
42446 non_halo_doubled_face_element_pt[iiface + 1];
42447 face_element_done[repeated_ele_face_pt] = true;
42448 // ... and increase the number of sorted face elements
42449 nsorted_face_elements += 2;
42450
42451 break;
42452 }
42453
42454 } // if (!face_element_done[[ele_face_pt])
42455 } // for (iiface<nnon_halo_doubled_face_ele)
42456 } while (face_element_added &&
42457 (nsorted_face_elements < nnon_halo_doubled_face_ele));
42458
42459 // -------------------------------------------------------------
42460 // At this point we already have a sorted set of nodes and can
42461 // be used to peform the unrefinement and refinement procedures
42462 // -------------------------------------------------------------
42463
42464 // Get the number of nodes on the list
42465 const unsigned nlocal_nodes = local_vertex_nodes.size();
42466 // Change representation to vector for easy of handling ...
42467 local_tmp_vector_vertex_node.resize(nlocal_nodes);
42468
42469 // Copy the vertices of the nodes
42470 unsigned counter = 0;
42471 std::set<Vector<double>>::iterator it_vertex;
42472 for (it_vertex = local_vertex_nodes.begin();
42473 it_vertex != local_vertex_nodes.end();
42474 it_vertex++)
42475 {
42476 local_tmp_vector_vertex_node[counter].resize(3);
42477 local_tmp_vector_vertex_node[counter][0] = (*it_vertex)[0];
42478 local_tmp_vector_vertex_node[counter][1] = (*it_vertex)[1];
42479 local_tmp_vector_vertex_node[counter][2] = (*it_vertex)[2];
42480 counter++;
42481 }
42482
42483 // The unrefinement and refinement process needs to be applied
42484 // from the bottom-left node since the internal open curve could
42485 // lie on the shared boundaries
42486 if (local_tmp_vector_vertex_node[nlocal_nodes - 1][2] <
42487 local_tmp_vector_vertex_node[0][2])
42488 {
42489 std::reverse(local_tmp_vector_vertex_node.begin(),
42490 local_tmp_vector_vertex_node.end());
42491 }
42492 else if (local_tmp_vector_vertex_node[nlocal_nodes - 1][2] ==
42493 local_tmp_vector_vertex_node[0][2])
42494 {
42495 if (local_tmp_vector_vertex_node[nlocal_nodes - 1][1] <
42496 local_tmp_vector_vertex_node[0][1])
42497 {
42498 std::reverse(local_tmp_vector_vertex_node.begin(),
42499 local_tmp_vector_vertex_node.end());
42500 }
42501 }
42502
42503 // ****************************************************************
42504 // 3) Create the vertices along the boundary using the target
42505 // area to define the distance among them
42506 // ****************************************************************
42507
42508 // Clear the local containter to recover the nodes ordered using
42509 // the zeta value
42510 local_vertex_nodes.clear();
42511
42512 // At the end of each unrefinement/refinement step store the new
42513 // nodes on the set that will give rise to the vertices of the
42514 // new polyline representation
42515 const unsigned nnew_nodes = local_tmp_vector_vertex_node.size();
42516 for (unsigned i = 0; i < nnew_nodes; i++)
42517 {
42518 vertex_coord[0] = local_tmp_vector_vertex_node[i][0];
42519 vertex_coord[1] = local_tmp_vector_vertex_node[i][1];
42520 vertex_coord[2] = local_tmp_vector_vertex_node[i][2];
42521 vertex_nodes.insert(vertex_coord); // Global container
42522 local_vertex_nodes.insert(vertex_coord);
42523 }
42524
42525#ifdef OOMPH_HAS_MPI
42526 if (this->is_mesh_distributed())
42527 {
42528 // Add the set of vertices for the boundary, this will help to
42529 // detect if we need to deal with sub_boundaries and
42530 // sub_polylines representations
42531 sub_vertex_nodes.push_back(local_vertex_nodes);
42532 // Increase the counter for sub_boundaries
42533 nsub_boundaries++;
42534
42535 // Mark if the polyline created by these vertices will be used
42536 // as a shared boundary or as an internal boundary
42537 if (both_root_face_elements_are_nonhalo)
42538 {
42539 internal_to_shared_boundary.push_back(false);
42540 }
42541 else
42542 {
42543 internal_to_shared_boundary.push_back(true);
42544 }
42545 }
42546#endif
42547
42548 } // while(nsorted_face_elements < nnon_halo_doubled_face_ele)
42549 // This while is in charge of sorting all the face elements to
42550 // create the new representation of the polyline (also deals
42551 // with the sub-boundary cases)
42552
42553 // Now turn into vector for ease of handling...
42554 const unsigned npoly_vertex = vertex_nodes.size();
42555 tmp_vector_vertex_node.resize(npoly_vertex);
42556 unsigned count = 0;
42557 std::set<Vector<double>>::iterator it;
42558 for (it = vertex_nodes.begin(); it != vertex_nodes.end(); ++it)
42559 {
42560 tmp_vector_vertex_node[count].resize(3);
42561 tmp_vector_vertex_node[count][0] = (*it)[0];
42562 tmp_vector_vertex_node[count][1] = (*it)[1];
42563 tmp_vector_vertex_node[count][2] = (*it)[2];
42564 ++count;
42565 }
42566
42567#ifdef OOMPH_HAS_MPI
42568 // Check that the number of set of vertices marked to be shared or
42569 // internal boundaries be the same as the total number of
42570 // sub-boundaries
42571#ifdef PARANOID
42572 const unsigned nsub_boundaries_set = sub_vertex_nodes.size();
42573 const unsigned ninternal_to_shared_boundaries =
42574 internal_to_shared_boundary.size();
42575 if (nsub_boundaries_set != ninternal_to_shared_boundaries)
42576 {
42577 std::ostringstream error_message;
42578 error_message
42579 << "The number of found sub-boundaries and the number of marked "
42580 << "internal\nboundaries are different\n"
42581 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
42582 << "Number of marked internal boundaries: ("
42583 << ninternal_to_shared_boundaries << ")\n\n";
42584 throw OomphLibError(
42585 error_message.str(),
42586 "RefineableTriangleMesh::update_open_curve_after_restart()",
42587 OOMPH_EXCEPTION_LOCATION);
42588 }
42589#endif
42590
42591 // --------- Stuff for the sub_boundaries ----- Begin section -------
42592#ifdef PARANOID
42593 if (nsub_boundaries_set != nsub_boundaries)
42594 {
42595 std::ostringstream error_message;
42596 error_message
42597 << "The number of found sub-boundaries and the number of counted\n"
42598 << "sub-boundaries are different:\n"
42599 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
42600 << "Number of counted sub-boundaries: (" << nsub_boundaries
42601 << ")\n\n";
42602 throw OomphLibError(
42603 error_message.str(),
42604 "RefineableTriangleMesh::update_open_curve_after_restart()",
42605 OOMPH_EXCEPTION_LOCATION);
42606 }
42607#endif
42608
42609 // Verify if need to deal with sub_boundaries
42610 if (this->is_mesh_distributed() && nsub_boundaries > 1)
42611 {
42612 // Mark the boundary as been splitted in the partition process
42613 this->Boundary_was_splitted[bound] = true;
42614 // Resize the vector to store the info. of sub-boundaries
42615 sub_tmp_vector_vertex_node.resize(nsub_boundaries);
42616 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
42617 {
42618 // Turn info. into vector for ease of handling...
42619 const unsigned nsubpoly_vertex = sub_vertex_nodes[isub].size();
42620 sub_tmp_vector_vertex_node[isub].resize(nsubpoly_vertex);
42621 unsigned subcount = 0;
42622 std::set<Vector<double>>::iterator subit;
42623 for (subit = sub_vertex_nodes[isub].begin();
42624 subit != sub_vertex_nodes[isub].end();
42625 ++subit)
42626 {
42627 sub_tmp_vector_vertex_node[isub][subcount].resize(3);
42628 sub_tmp_vector_vertex_node[isub][subcount][0] = (*subit)[0];
42629 sub_tmp_vector_vertex_node[isub][subcount][1] = (*subit)[1];
42630 sub_tmp_vector_vertex_node[isub][subcount][2] = (*subit)[2];
42631 ++subcount;
42632 }
42633 }
42634 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
42635 // --------- Stuff for the sub_boundaries ----- End section ----------
42636#endif // OOMPH_HAS_MPI
42637
42638 // For further processing the three-dimensional vector has to be
42639 // reduced to a two-dimensional vector
42640 unsigned n_vertex = tmp_vector_vertex_node.size();
42641
42642 // Resize the vector for vectices
42643 vector_vertex_node.resize(n_vertex);
42644 for (unsigned i = 0; i < n_vertex; i++)
42645 {
42646 vector_vertex_node[i].resize(2);
42647 vector_vertex_node[i][0] = tmp_vector_vertex_node[i][1];
42648 vector_vertex_node[i][1] = tmp_vector_vertex_node[i][2];
42649 }
42650
42651#ifdef OOMPH_HAS_MPI
42652 // --------- Stuff for the sub_boundaries ----- Begin section ----------
42653 // Verify if need to deal with sub_boundaries
42654 if (this->is_mesh_distributed() && nsub_boundaries > 1)
42655 {
42656 // For further processing the three-dimensional vector
42657 // has to be reduced to a two-dimensional vector
42658 // Resize the vector to store the info. of sub-boundaries
42659 sub_vector_vertex_node.resize(nsub_boundaries);
42660 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
42661 {
42662 const unsigned subn_vertex = sub_tmp_vector_vertex_node[isub].size();
42663 // Resize the vector for vectices
42664 sub_vector_vertex_node[isub].resize(subn_vertex);
42665 for (unsigned i = 0; i < subn_vertex; i++)
42666 {
42667 sub_vector_vertex_node[isub][i].resize(2);
42668 sub_vector_vertex_node[isub][i][0] =
42669 sub_tmp_vector_vertex_node[isub][i][1];
42670 sub_vector_vertex_node[isub][i][1] =
42671 sub_tmp_vector_vertex_node[isub][i][2];
42672 }
42673 }
42674 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
42675
42676 // We already have the info. for the sub-boundaries (if necessary) and
42677 // then we can create the sub-boundaries representations to ease the
42678 // generation of the mesh by Triangle
42679
42680 // --------- Stuff for the sub_boundaries ----- End section ------------
42681#endif // OOMPH_HAS_MPI
42682
42683 // *********************************************************************
42684 // 4) Check for contiguousness
42685 // *********************************************************************
42686#ifdef OOMPH_HAS_MPI
42687 // Only perform this checking if the mesh is not distributed
42688 // When the mesh is distributed the polylines continuity is
42689 // addressed with the sort_polylines_helper() method
42690 if (!this->is_mesh_distributed())
42691#endif
42692 {
42693 if (cs > 0)
42694 {
42695 // Final end point of previous line
42696 Vector<double> final_vertex_of_previous_segment;
42697 unsigned n_prev_vertex =
42698 open_curve_pt->curve_section_pt(cs - 1)->nvertex();
42699 final_vertex_of_previous_segment =
42700 open_curve_pt->polyline_pt(cs - 1)->vertex_coordinate(
42701 n_prev_vertex - 1);
42702
42703 unsigned prev_seg_boundary_id =
42704 open_curve_pt->curve_section_pt(cs - 1)->boundary_id();
42705
42706 // Find the error between the final vertex of the previous
42707 // line and the first vertex of the current line
42708 double error = 0.0;
42709 for (unsigned i = 0; i < 2; i++)
42710 {
42711 const double dist = final_vertex_of_previous_segment[i] -
42712 (*vector_vertex_node.begin())[i];
42713 error += dist * dist;
42714 }
42715 error = sqrt(error);
42716
42717 // If the error is bigger than the tolerance then
42718 // we probably need to reverse, but better check
42720 {
42721 // Find the error between the final vertex of the previous
42722 // line and the last vertex of the current line
42723 double rev_error = 0.0;
42724 for (unsigned i = 0; i < 2; i++)
42725 {
42726 const double dist = final_vertex_of_previous_segment[i] -
42727 (*--vector_vertex_node.end())[i];
42728 rev_error += dist * dist;
42729 }
42730 rev_error = sqrt(rev_error);
42731
42732 if (rev_error >
42734 {
42735 // It could be possible that the first segment be reversed and we
42736 // did not notice it because this check does not apply for the
42737 // first segment. We can verify if the first segment is reversed
42738 // by using the vertex number 1
42739 if (cs == 1)
42740 {
42741 // Initial end point of previous line
42742 Vector<double> initial_vertex_of_previous_segment;
42743
42744 initial_vertex_of_previous_segment =
42745 open_curve_pt->polyline_pt(cs - 1)->vertex_coordinate(0);
42746
42747 unsigned prev_seg_boundary_id =
42748 open_curve_pt->curve_section_pt(cs - 1)->boundary_id();
42749
42750 // Find the error between the initial vertex of the previous
42751 // line and the first vertex of the current line
42752 double error = 0.0;
42753 for (unsigned i = 0; i < 2; i++)
42754 {
42755 const double dist = initial_vertex_of_previous_segment[i] -
42756 (*vector_vertex_node.begin())[i];
42757 error += dist * dist;
42758 }
42759 error = sqrt(error); // Reversed only the previous one
42760
42761 // If the error is bigger than the tolerance then
42762 // we probably need to reverse, but better check
42763 if (error >
42765 {
42766 // Find the error between the final vertex of the previous
42767 // line and the last vertex of the current line
42768 double rev_error = 0.0;
42769 for (unsigned i = 0; i < 2; i++)
42770 {
42771 const double dist = initial_vertex_of_previous_segment[i] -
42772 (*--vector_vertex_node.end())[i];
42773 rev_error += dist * dist;
42774 }
42775 rev_error = sqrt(rev_error); // Reversed both the current
42776 // one and the previous one
42777
42778 if (rev_error >
42780 {
42781 std::ostringstream error_stream;
42782 error_stream
42783 << "The distance between the first node of the current\n"
42784 << "line segment (boundary " << bound
42785 << ") and either end of "
42786 << "the previous line segment\n"
42787 << "(boundary " << prev_seg_boundary_id
42788 << ") is bigger than"
42789 << " the desired tolerance "
42791 << ".\n"
42792 << "This suggests that the polylines defining the "
42793 "polygonal\n"
42794 << "representation are not properly ordered.\n"
42795 << "Fail on last vertex of polyline: ("
42796 << prev_seg_boundary_id
42797 << ") and\nfirst vertex of polyline (" << bound
42798 << ").\nThis should have failed when first trying to "
42799 << "construct the\npolygon.\n";
42800 throw OomphLibError(error_stream.str(),
42801 "RefineableTriangleMesh::update_open_"
42802 "curve_after_restart()",
42803 OOMPH_EXCEPTION_LOCATION);
42804 }
42805 else
42806 {
42807 // Reverse both
42808 // Reverse the current vector to line up with the previous
42809 // one
42810 std::reverse(vector_vertex_node.begin(),
42811 vector_vertex_node.end());
42812 open_curve_pt->polyline_pt(cs - 1)->reverse();
42813 }
42814 }
42815 else
42816 {
42817 // Reverse the previous one
42818 open_curve_pt->polyline_pt(cs - 1)->reverse();
42819 }
42820
42821 } // if (cs == 1)
42822 else
42823 {
42824 std::ostringstream error_stream;
42825 error_stream
42826 << "The distance between the first node of the current\n"
42827 << "line segment (boundary " << bound
42828 << ") and either end of "
42829 << "the previous line segment\n"
42830 << "(boundary " << prev_seg_boundary_id
42831 << ") is bigger than the "
42832 << "desired tolerance "
42834 << ".\n"
42835 << "This suggests that the polylines defining the polygonal\n"
42836 << "representation are not properly ordered.\n"
42837 << "Fail on last vertex of polyline: ("
42838 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
42839 << bound << ").\n"
42840 << "This should have failed when first trying to construct "
42841 "the\n"
42842 << "polygon.\n";
42843 throw OomphLibError(
42844 error_stream.str(),
42845 "RefineableTriangleMesh::update_open_curve_after_restart()",
42846 OOMPH_EXCEPTION_LOCATION);
42847 }
42848 }
42849 else
42850 {
42851 // Reverse the current vector to line up with the previous one
42852 std::reverse(vector_vertex_node.begin(),
42853 vector_vertex_node.end());
42854 }
42855 } // error
42856 } // (cs > 0)
42857 } // is mesh not distributed
42858
42859 // DEBP(applied_area_length_constraint);
42860 // DEBP(p);
42861 // getchar();
42862 // *********************************************************************
42863 // 5) Update the polylines representation
42864 // *********************************************************************
42865 // if (applied_area_length_constraint)
42866 // If only applied when there is a change then it keeps the
42867 // previous polyline representation, it means, it does not delete
42868 // the boundaries that are not part of the domain. We must update
42869 // the boundary representation
42870 {
42871 n_vertex = vector_vertex_node.size();
42872
42873 // Now update the polyline according to the new vertices
42874 // The new one representation
42875 TriangleMeshPolyLine* tmp_polyline_pt =
42876 new TriangleMeshPolyLine(vector_vertex_node, bound);
42877
42878 // Create a temporal "curve section" version of the recently created
42879 // polyline
42880 TriangleMeshCurveSection* tmp_curve_section_pt = tmp_polyline_pt;
42881
42882 // Tolerance below which the middle point can be deleted
42883 // (ratio of deflection to element length)
42884 double unrefinement_tolerance =
42885 open_curve_pt->polyline_pt(cs)->unrefinement_tolerance();
42886
42887 // Tolerance to add points
42888 double refinement_tolerance =
42889 open_curve_pt->polyline_pt(cs)->refinement_tolerance();
42890
42891 // Establish refinement and unrefinement tolerance
42892 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
42893 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
42894
42895 // Establish the maximum length constraint
42896 double maximum_length =
42897 open_curve_pt->polyline_pt(cs)->maximum_length();
42898 tmp_polyline_pt->set_maximum_length(maximum_length);
42899
42900 if (n_vertex >= 2)
42901 {
42902 // Pass the connection information from the old polyline to the
42903 // new one
42904 this->copy_connection_information(open_curve_pt->polyline_pt(cs),
42905 tmp_curve_section_pt);
42906 }
42907
42908 // Now update the polyline according to the new vertices but first
42909 // check if the object is allowed to delete the representation or
42910 // if it should be done by other object
42911 bool delete_it_on_destructor = false;
42912
42913 std::set<TriangleMeshCurveSection*>::iterator it =
42914 this->Free_curve_section_pt.find(open_curve_pt->curve_section_pt(cs));
42915
42916 if (it != this->Free_curve_section_pt.end())
42917 {
42918 this->Free_curve_section_pt.erase(it);
42919 delete open_curve_pt->curve_section_pt(cs);
42920 delete_it_on_destructor = true;
42921 }
42922
42923 // *****************************************************************
42924 // Copying the new representation
42925 open_curve_pt->curve_section_pt(cs) = tmp_polyline_pt;
42926
42927 // Update the Boundary - Polyline map
42928 this->Boundary_curve_section_pt[bound] =
42929 open_curve_pt->curve_section_pt(cs);
42930
42931 if (delete_it_on_destructor)
42932 {
42933 this->Free_curve_section_pt.insert(
42934 open_curve_pt->curve_section_pt(cs));
42935 }
42936
42937#ifdef OOMPH_HAS_MPI
42938
42939 // If there are not sub-boundaries mark the boundary if need to be
42940 // trated as shared or as internal boundary
42941 if (this->is_mesh_distributed() && nsub_boundaries == 1)
42942 {
42943 // Clear all previous stored data
42944 this->Boundary_marked_as_shared_boundary[bound].clear();
42945
42946 // .. and store the flag for the boundary
42947 this->Boundary_marked_as_shared_boundary[bound].push_back(
42948 internal_to_shared_boundary[0]);
42949 }
42950 // --------- Stuff for the sub_boundaries ----- Begin section --------
42951 // Verify if need to deal with sub_boundaries
42952 else if (this->is_mesh_distributed() && nsub_boundaries > 1)
42953 {
42954 // Create temporary representations for the boundaries, only to
42955 // create the mesh when calling Triangle
42956 // Clear all previous stored data
42957 this->Boundary_subpolylines[bound].clear();
42958 // Now create storage for the sub-boundaries
42959 this->Boundary_subpolylines[bound].resize(nsub_boundaries);
42960
42961 // Clear all previous stored data
42962 this->Boundary_marked_as_shared_boundary[bound].clear();
42963 // Create storage to mark the internal boundaries as shared
42964 // boundaries
42965 this->Boundary_marked_as_shared_boundary[bound].resize(
42966 nsub_boundaries);
42967 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
42968 {
42969 // Now update the polyline according to the sub set of
42970 // vertices, set the chunk number of the polyline
42971 TriangleMeshPolyLine* sub_tmp_polyline_pt =
42973 sub_vector_vertex_node[isub], bound, isub);
42974
42975 // Add the sub-polyline to the container to represent the
42976 // boundary in parts
42977 this->Boundary_subpolylines[bound][isub] = sub_tmp_polyline_pt;
42978
42979 // Copy the flag that mark the boundary as internal or as
42980 // shared bound
42981 this->Boundary_marked_as_shared_boundary[bound][isub] =
42982 internal_to_shared_boundary[isub];
42983
42984 // No need to send the unrefinement/refinement and maximum
42985 // length constraints since these are only temporary
42986 // representations
42987 }
42988
42989 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
42990 // --------- Stuff for the sub_boundaries ----- End section ---------
42991#endif // OOMPH_HAS_MPI
42992
42993 } // update polyline representation
42994
42995 // Delete the allocated memory for the geometric object
42996 // that represents the curvilinear boundary
42997 delete mesh_geom_obj_pt;
42998
42999 } // npolyline
43000
43001 // Cleanup the face mesh
43002 for (unsigned p = 0; p < ncurve_section; p++)
43003 {
43004 face_mesh_pt[p]->flush_node_storage();
43005 delete face_mesh_pt[p];
43006 }
43007 }
43008
43009#ifdef OOMPH_HAS_MPI
43010 //======================================================================
43011 /// Updates the shared polylines representation after restart
43012 //======================================================================
43013 template<class ELEMENT>
43015 Vector<TriangleMeshPolyLine*>& vector_polyline_pt)
43016 {
43017 // Go through all the shared boundaries/polylines
43018 const unsigned npolylines = vector_polyline_pt.size();
43019 for (unsigned pp = 0; pp < npolylines; pp++)
43020 {
43021 // Get the boundary of the current polyline
43022 const unsigned b = vector_polyline_pt[pp]->boundary_id();
43023
43024 // Get the edges of the shared boundary elements that create the
43025 // shared boundary and store the shared boundary elements from where
43026 // were created
43027 std::map<std::pair<Node*, Node*>, FiniteElement*> halo_edge_element_pt;
43028 std::map<std::pair<Node*, Node*>, FiniteElement*> nonhalo_edge_element_pt;
43029
43030 // Store the nodes that define the edges
43031 Vector<Node*> halo_edge_nodes_pt;
43032 Vector<Node*> nonhalo_edge_nodes_pt;
43033
43034 // Go through the shared boundary elements and store their edges
43035 const unsigned nshared_bound_ele = this->nshared_boundary_element(b);
43036 for (unsigned e = 0; e < nshared_bound_ele; e++)
43037 {
43038 // Get the shared boundary element
43039 FiniteElement* current_ele_pt = this->shared_boundary_element_pt(b, e);
43040
43041 // Get the corner nodes, the first three nodes
43042 Node* first_node_pt = current_ele_pt->node_pt(0);
43043 Node* second_node_pt = current_ele_pt->node_pt(1);
43044 Node* third_node_pt = current_ele_pt->node_pt(2);
43045
43046 // Check if the elements is halo
43047 if (!current_ele_pt->is_halo())
43048 {
43049 // Store the edges
43050 nonhalo_edge_nodes_pt.push_back(first_node_pt);
43051 nonhalo_edge_nodes_pt.push_back(second_node_pt);
43052
43053 nonhalo_edge_nodes_pt.push_back(second_node_pt);
43054 nonhalo_edge_nodes_pt.push_back(third_node_pt);
43055
43056 nonhalo_edge_nodes_pt.push_back(third_node_pt);
43057 nonhalo_edge_nodes_pt.push_back(first_node_pt);
43058
43059 // Store the info. of the element used to create these edges
43060 std::pair<Node*, Node*> edge1 =
43061 std::make_pair(first_node_pt, second_node_pt);
43062 nonhalo_edge_element_pt[edge1] = current_ele_pt;
43063
43064 std::pair<Node*, Node*> edge2 =
43065 std::make_pair(second_node_pt, third_node_pt);
43066 nonhalo_edge_element_pt[edge2] = current_ele_pt;
43067
43068 std::pair<Node*, Node*> edge3 =
43069 std::make_pair(third_node_pt, first_node_pt);
43070 nonhalo_edge_element_pt[edge3] = current_ele_pt;
43071 }
43072 else
43073 {
43074 // Store the edges
43075 halo_edge_nodes_pt.push_back(first_node_pt);
43076 halo_edge_nodes_pt.push_back(second_node_pt);
43077
43078 halo_edge_nodes_pt.push_back(second_node_pt);
43079 halo_edge_nodes_pt.push_back(third_node_pt);
43080
43081 halo_edge_nodes_pt.push_back(third_node_pt);
43082 halo_edge_nodes_pt.push_back(first_node_pt);
43083
43084 // Store the info. of the element used to create these edges
43085 std::pair<Node*, Node*> edge1 =
43086 std::make_pair(first_node_pt, second_node_pt);
43087 halo_edge_element_pt[edge1] = current_ele_pt;
43088
43089 std::pair<Node*, Node*> edge2 =
43090 std::make_pair(second_node_pt, third_node_pt);
43091 halo_edge_element_pt[edge2] = current_ele_pt;
43092
43093 std::pair<Node*, Node*> edge3 =
43094 std::make_pair(third_node_pt, first_node_pt);
43095 halo_edge_element_pt[edge3] = current_ele_pt;
43096 }
43097
43098 } // for (e < nshared_bound_ele)
43099
43100 // Filter the edges that give rise to a shared boundary
43101
43102 // Mark the done edges
43103 std::map<std::pair<Node*, Node*>, bool> edge_done;
43104
43105 // Storage for the edges shared by the elements
43106 Vector<std::pair<Node*, Node*>> unsorted_edges;
43107
43108 // Storage for the elements that created the unsorted edges (two
43109 // elements, one at each side of the shared boundary)
43110 Vector<Vector<FiniteElement*>> unsorted_edges_elements_pt;
43111
43112 const unsigned nnonhalo_edge_nodes = nonhalo_edge_nodes_pt.size();
43113 for (unsigned i = 0; i < nnonhalo_edge_nodes; i += 2)
43114 {
43115 Vector<Node*> currenti_edge(2);
43116 currenti_edge[0] = nonhalo_edge_nodes_pt[i];
43117 currenti_edge[1] = nonhalo_edge_nodes_pt[i + 1];
43118
43119 // Create the edge (both nodes that make the edge)
43120 std::pair<Node*, Node*> new_edge =
43121 std::make_pair(currenti_edge[0], currenti_edge[1]);
43122
43123 if (!edge_done[new_edge])
43124 {
43125 const unsigned nhalo_edge_nodes = halo_edge_nodes_pt.size();
43126 for (unsigned j = 0; j < nhalo_edge_nodes; j += 2)
43127 {
43128 Vector<Node*> currentj_edge(2);
43129 currentj_edge[0] = halo_edge_nodes_pt[j];
43130 currentj_edge[1] = halo_edge_nodes_pt[j + 1];
43131
43132 // Comparing pointer of nodes
43133 if (currenti_edge[0] == currentj_edge[0] &&
43134 currenti_edge[1] == currentj_edge[1])
43135 {
43136 // Store the edge in the proper container
43137 unsorted_edges.push_back(new_edge);
43138
43139 // Get the elements associated with the edges
43140 Vector<FiniteElement*> tmp_edge_element_pt;
43141
43142 FiniteElement* nonhalo_ele_pt = nonhalo_edge_element_pt[new_edge];
43143 FiniteElement* halo_ele_pt = halo_edge_element_pt[new_edge];
43144
43145 tmp_edge_element_pt.push_back(nonhalo_ele_pt);
43146 tmp_edge_element_pt.push_back(halo_ele_pt);
43147
43148 // Store the elements associated with the edge
43149 unsorted_edges_elements_pt.push_back(tmp_edge_element_pt);
43150
43151 // Mark the edge as done
43152 edge_done[new_edge] = true;
43153
43154 // Break the loop for (j < nedge_node)
43155 break;
43156
43157 } // equal edge
43158
43159 // Comparing pointer of nodes (reversed)
43160 else if (currenti_edge[0] == currentj_edge[1] &&
43161 currenti_edge[1] == currentj_edge[0])
43162 {
43163 // Create the edge (both nodes that make the edge)
43164 std::pair<Node*, Node*> new_edge =
43165 std::make_pair(currenti_edge[0], currenti_edge[1]);
43166
43167 // Store the edge in the proper container
43168 unsorted_edges.push_back(new_edge);
43169
43170 // Create the (reversed) edge (both nodes that make the edge)
43171 std::pair<Node*, Node*> rev_new_edge =
43172 std::make_pair(currentj_edge[0], currentj_edge[1]);
43173
43174 // Get the elements associated with the edge
43175 Vector<FiniteElement*> tmp_edge_element_pt;
43176
43177 FiniteElement* nonhalo_ele_pt = nonhalo_edge_element_pt[new_edge];
43178 FiniteElement* halo_ele_pt = halo_edge_element_pt[rev_new_edge];
43179
43180 tmp_edge_element_pt.push_back(nonhalo_ele_pt);
43181 tmp_edge_element_pt.push_back(halo_ele_pt);
43182
43183 // Store the elements associated with the edge
43184 unsorted_edges_elements_pt.push_back(tmp_edge_element_pt);
43185
43186 // Mark the edge as done
43187 edge_done[new_edge] = true;
43188
43189 // Break the loop for (j < nedge_node)
43190 break;
43191
43192 } // if (equal edge)
43193
43194 } // for (j < nhalo_edge_nodes)
43195
43196 } // if (!edge_done[new_edge])
43197
43198 } // for (i < nnonhalo_edge_nodes)
43199
43200 // We already have the edges that make the shared boundary (and the
43201 // elements)
43202 // Sort them to create a contiguous boundary
43203
43204 // Mark the already sorted edges
43205 std::map<std::pair<Node*, Node*>, bool> edge_sorted;
43206
43207 const unsigned nunsorted_edges = unsorted_edges.size();
43208
43209#ifdef PARANOID
43210 // The number of unsorted edges must be the same as the number of
43211 // shared_boundary element / 2
43212 if (nshared_bound_ele / 2 != nunsorted_edges)
43213 {
43214 std::ostringstream error_message;
43215 error_message
43216 << "The number of shared boundary elements (" << nshared_bound_ele
43217 << ") is not the double\nof the number of unsorted edges ("
43218 << nunsorted_edges << ") for the current boundary (" << b << ")\n\n";
43219 throw OomphLibError(
43220 error_message.str(),
43221 "RefineableTriangleMesh::update_shared_curve_after_restart()",
43222 OOMPH_EXCEPTION_LOCATION);
43223 }
43224#endif
43225
43226 unsigned nsorted_edges = 0;
43227
43228 // Storing for the sorting nodes extracted from the edges, and
43229 // then used to update the polyline
43230 std::list<Node*> sorted_nodes;
43231
43232 // Storing for the edges elements
43233 std::list<FiniteElement*> sorted_edges_elements_pt;
43234
43235 // Get the root edge
43236 std::pair<Node*, Node*> edge = unsorted_edges[0];
43237 nsorted_edges++;
43238
43239 // Mark edge as done
43240 edge_sorted[edge] = true;
43241
43242 // The initial and final node on the list
43243 Node* first_node_pt = edge.first;
43244 Node* last_node_pt = edge.second;
43245
43246 // Push back on the list the new edge (nodes)
43247 sorted_nodes.push_back(first_node_pt);
43248 sorted_nodes.push_back(last_node_pt);
43249
43250 // Store the elements for the current edge
43251 sorted_edges_elements_pt.push_back(unsorted_edges_elements_pt[0][0]);
43252 sorted_edges_elements_pt.push_back(unsorted_edges_elements_pt[0][1]);
43253
43254 // Iterate while the number of sorted edges be less than the number of
43255 // unsorted edges
43256 while (nsorted_edges < nunsorted_edges)
43257 {
43258 // Flag to indicate when a node was added
43259 bool node_added = false;
43260
43261 // Start from the next edge since we have already added the
43262 // previous one as the initial edge
43263 for (unsigned iedge = 1; iedge < nunsorted_edges; iedge++)
43264 {
43265 edge = unsorted_edges[iedge];
43266
43267 // If edge not done
43268 if (!edge_sorted[edge])
43269 {
43270 // Get each individual node
43271 Node* left_node_pt = edge.first;
43272 Node* right_node_pt = edge.second;
43273
43274 if (left_node_pt == first_node_pt)
43275 {
43276 // Push front the new node
43277 sorted_nodes.push_front(right_node_pt);
43278 first_node_pt = right_node_pt;
43279 node_added = true;
43280
43281 // Store the elements for the current edge
43282 sorted_edges_elements_pt.push_front(
43283 unsorted_edges_elements_pt[iedge][1]);
43284 sorted_edges_elements_pt.push_front(
43285 unsorted_edges_elements_pt[iedge][0]);
43286 }
43287 else if (left_node_pt == last_node_pt)
43288 {
43289 // Push back the new node
43290 sorted_nodes.push_back(right_node_pt);
43291 last_node_pt = right_node_pt;
43292 node_added = true;
43293
43294 // Store the elements for the current edge
43295 sorted_edges_elements_pt.push_back(
43296 unsorted_edges_elements_pt[iedge][0]);
43297 sorted_edges_elements_pt.push_back(
43298 unsorted_edges_elements_pt[iedge][1]);
43299 }
43300 else if (right_node_pt == first_node_pt)
43301 {
43302 // Push front the new node
43303 sorted_nodes.push_front(left_node_pt);
43304 first_node_pt = left_node_pt;
43305 node_added = true;
43306
43307 // Store the elements for the current edge
43308 sorted_edges_elements_pt.push_front(
43309 unsorted_edges_elements_pt[iedge][1]);
43310 sorted_edges_elements_pt.push_front(
43311 unsorted_edges_elements_pt[iedge][0]);
43312 }
43313 else if (right_node_pt == last_node_pt)
43314 {
43315 // Push back the new node
43316 sorted_nodes.push_back(left_node_pt);
43317 last_node_pt = left_node_pt;
43318 node_added = true;
43319
43320 // Store the elements for the current edge
43321 sorted_edges_elements_pt.push_back(
43322 unsorted_edges_elements_pt[iedge][0]);
43323 sorted_edges_elements_pt.push_back(
43324 unsorted_edges_elements_pt[iedge][1]);
43325 }
43326
43327 if (node_added)
43328 {
43329 // Mark as done only if one of its nodes has been
43330 // added to the list
43331 edge_sorted[edge] = true;
43332 nsorted_edges++;
43333
43334 // Break the for
43335 break;
43336 }
43337
43338 } // if (!edge_done[edge])
43339 } // for (iedge < nunsorted_edges)
43340 } // while (nsorted_edges < nunsorted_edges)
43341
43342 // At this point we already have a sorted list of nodes, get the
43343 // vertices from them and store them in a vector container
43344
43345 // Get the number of nodes on the list
43346 unsigned nvertex = sorted_nodes.size();
43347 // The vector to store the vertices (assign space)
43348 Vector<Vector<double>> polyline_vertices(nvertex);
43349
43350 // Copy the vertices of the nodes
43351 unsigned counter = 0;
43352 for (std::list<Node*>::iterator it_nodes = sorted_nodes.begin();
43353 it_nodes != sorted_nodes.end();
43354 it_nodes++)
43355 {
43356 polyline_vertices[counter].resize(2);
43357 polyline_vertices[counter][0] = (*it_nodes)->x(0);
43358 polyline_vertices[counter][1] = (*it_nodes)->x(1);
43359 counter++;
43360 }
43361
43362 // Before going to the unrefinement or refinement process check that
43363 // all processors start from the same vertex. Start from the bottom
43364 // left vertex
43365 if (polyline_vertices[nvertex - 1][1] < polyline_vertices[0][1])
43366 {
43367 std::reverse(polyline_vertices.begin(), polyline_vertices.end());
43368 }
43369 else if (polyline_vertices[nvertex - 1][1] == polyline_vertices[0][1])
43370 {
43371 if (polyline_vertices[nvertex - 1][0] < polyline_vertices[0][0])
43372 {
43373 std::reverse(polyline_vertices.begin(), polyline_vertices.end());
43374 }
43375 }
43376
43377 // Create the polyline associated with this edge
43378 TriangleMeshPolyLine* new_polyline_pt =
43379 new TriangleMeshPolyLine(polyline_vertices, b);
43380
43381 // Get the curve section representation
43382 TriangleMeshCurveSection* curve_section_pt = vector_polyline_pt[pp];
43383
43384 // Copy the connection information from the old shared polyline
43385 // to the new one
43386 this->copy_connection_information(curve_section_pt, new_polyline_pt);
43387
43388 // Now update the polyline according to the new vertices but first
43389 // check if the object is allowed to delete the representation
43390 // or if it should be done by other object
43391 bool delete_it_on_destructor = false;
43392
43393 // Establish the element as being deleted by the destructor of
43394 // the class
43395 std::set<TriangleMeshCurveSection*>::iterator it =
43396 this->Free_curve_section_pt.find(curve_section_pt);
43397
43398 if (it != this->Free_curve_section_pt.end())
43399 {
43400 this->Free_curve_section_pt.erase(it);
43401 delete curve_section_pt;
43402 delete_it_on_destructor = true;
43403 }
43404
43405 // Copy the new representation
43406 vector_polyline_pt[pp] = new_polyline_pt;
43407
43408 // Get the new curve section representation
43409 TriangleMeshCurveSection* new_curve_section_pt = vector_polyline_pt[pp];
43410
43411 // Update the Boundary - Polyline map
43412 this->Boundary_curve_section_pt[b] = new_curve_section_pt;
43413
43414 if (delete_it_on_destructor)
43415 {
43416 this->Free_curve_section_pt.insert(new_curve_section_pt);
43417 }
43418
43419 } // for (pp < npoly)
43420 }
43421
43422 //===================================================================
43423 // Fill the boundary elements structures when dealing with
43424 // shared boundaries that overlap internal boundaries. Document the
43425 // number of elements on the shared boundaries that go to internal
43426 // boundaries
43427 //===================================================================
43428 template<class ELEMENT>
43430 ELEMENT>::fill_boundary_elements_and_nodes_for_internal_boundaries()
43431 {
43432 // Dummy file
43433 std::ofstream some_file;
43434 fill_boundary_elements_and_nodes_for_internal_boundaries(some_file);
43435 }
43436
43437 //===================================================================
43438 // Fill the boundary elements structures when dealing with
43439 // shared boundaries that overlap internal boundaries
43440 //===================================================================
43441 template<class ELEMENT>
43444 std::ofstream& outfile)
43445 {
43446 // Get the number of processors
43447 const unsigned nproc = this->communicator_pt()->nproc();
43448 // Get the rank of the current processor
43449 unsigned my_rank = this->communicator_pt()->my_rank();
43450
43451 // Temporal name for the shared boundary overlaps structure
43452 std::map<unsigned, unsigned> shd_bnd_over_int_bnd =
43453 this->Shared_boundary_overlaps_internal_boundary;
43454
43455 // Register the internal boundary elements that where found to be
43456 // overlapped by shared boundaries
43457 std::set<unsigned> internal_boundary_overlaped;
43458
43459 // Document the number of elements and nodes associated to the
43460 // boundaries before filling elements and nodes
43461 if (outfile.is_open())
43462 {
43463 const unsigned nbound = this->nboundary();
43464 outfile << "Number of boundaries: " << nbound << "\n\n";
43465 outfile << "Number of elements and nodes associated to each "
43466 << "boundary before\nfilling elements and nodes\n\n";
43467 for (unsigned i = 0; i < nbound; i++)
43468 {
43469 outfile << "Boundary (" << i << ") Elements ("
43470 << this->nboundary_element(i) << ") "
43471 << "Nodes (" << this->nboundary_node(i) << ")\n";
43472 }
43473 }
43474
43475 // Storage for the shared boundaries in this processor
43476 std::set<unsigned> shared_boundaries_in_this_processor;
43477
43478 // Get the shared boundaries that this processor has with other
43479 // processors
43480 for (unsigned iproc = 0; iproc < nproc; iproc++)
43481 {
43482 // Work with other processors only
43483 if (iproc != my_rank)
43484 {
43485 // Get the number of boundaries shared with the "iproc"-th processor
43486 unsigned nshared_boundaries_with_iproc =
43487 this->nshared_boundaries(my_rank, iproc);
43488
43489 if (nshared_boundaries_with_iproc > 0)
43490 {
43491 // Get the boundaries ids shared with "iproc"-th processor
43492 Vector<unsigned> bound_shared_with_iproc;
43493 bound_shared_with_iproc = this->shared_boundaries_ids(my_rank, iproc);
43494
43495 // Loop over shared boundaries with "iproc"-th processor
43496 for (unsigned bs = 0; bs < nshared_boundaries_with_iproc; bs++)
43497 {
43498 unsigned bnd_id = bound_shared_with_iproc[bs];
43499 shared_boundaries_in_this_processor.insert(bnd_id);
43500 }
43501 }
43502 }
43503 }
43504
43505 // ------------------------------------------------------------------
43506 // Copy the boundary elements and nodes from the shared boundary to
43507 // the internal boundary it overlaps
43508 // ------------------------------------------------------------------
43509 // Go through the shared boundaries that overlap internal boundaries
43510 for (std::map<unsigned, unsigned>::iterator it =
43511 shd_bnd_over_int_bnd.begin();
43512 it != shd_bnd_over_int_bnd.end();
43513 it++)
43514 {
43515 // The shared boundary id that overlaps with an internal boundary
43516 const unsigned shd_bnd_id = (*it).first;
43517 // The internal boundary overlapped by the shared boundary
43518 const unsigned int_bnd_id = (*it).second;
43519
43520 // Check if the shared boundary exist in this processor
43521 std::set<unsigned>::iterator it_set =
43522 shared_boundaries_in_this_processor.find(shd_bnd_id);
43523 if (it_set != shared_boundaries_in_this_processor.end())
43524 {
43525 internal_boundary_overlaped.insert(int_bnd_id);
43526
43527 // -----------------------------------------------------------------
43528 // First work the nodes of the shared boundaries that should be
43529 // added to the internal boundaries
43530 const unsigned nbnd_node_shd_bnd = this->nboundary_node(shd_bnd_id);
43531
43532 // Document the number of nodes that will be passed to the internal
43533 // boundary from the current shared boundary
43534 if (outfile.is_open())
43535 {
43536 outfile << "\nPass info. from shared (" << shd_bnd_id
43537 << ") to internal (" << int_bnd_id << ")\n";
43538 outfile << "Number of shared boundary nodes: " << nbnd_node_shd_bnd
43539 << "\n";
43540 }
43541
43542 for (unsigned in = 0; in < nbnd_node_shd_bnd; in++)
43543 {
43544 // Get the boundary node
43545 Node* bnd_node_pt = this->boundary_node_pt(shd_bnd_id, in);
43546 // Add the node to the internal boundary
43547 this->add_boundary_node(int_bnd_id, bnd_node_pt);
43548 }
43549
43550 // -----------------------------------------------------------------
43551 // Second work the boundary elements
43552 // Get the number of boundary elements that should be copied to the
43553 // internal boundary
43554 const unsigned nbnd_ele_shd_bnd = this->nboundary_element(shd_bnd_id);
43555
43556 // Document the number of elements that will be passed to the
43557 // internal boundary from the current shared boundary
43558 if (outfile.is_open())
43559 {
43560 outfile << "Number of shared boundary elements: " << nbnd_ele_shd_bnd
43561 << "\n\n";
43562 }
43563
43564 // Go through the boundary elements in the shrared boundary and add
43565 // them to the boundary elements of the internal boundary
43566 for (unsigned ie = 0; ie < nbnd_ele_shd_bnd; ie++)
43567 {
43568 // Get the boundary element
43569 FiniteElement* bnd_ele_pt = this->boundary_element_pt(shd_bnd_id, ie);
43570 // Add the element to the boundary elements storage of the
43571 // internal boundary
43572 Boundary_element_pt[int_bnd_id].push_back(bnd_ele_pt);
43573 // Get the face index of the boundary
43574 int face_index = this->face_index_at_boundary(shd_bnd_id, ie);
43575 // Add the face index to the storage of the boundary
43576 Face_index_at_boundary[int_bnd_id].push_back(face_index);
43577
43578 } // for (ie < nbnd_ele_shd_bnd)
43579
43580 // If there are regions we need to fill the storage for regions too
43581 const unsigned nregions = this->nregion();
43582 if (nregions > 1)
43583 {
43584 for (unsigned ir = 0; ir < nregions; ir++)
43585 {
43586 // Get the region attribute
43587 const unsigned region_id =
43588 static_cast<unsigned>(this->Region_attribute[ir]);
43589
43590 // Loop over all elements on boundaries in region ir
43591 const unsigned nele_ir =
43592 this->nboundary_element_in_region(shd_bnd_id, region_id);
43593 for (unsigned ier = 0; ier < nele_ir; ier++)
43594 {
43595 // Get the boundary element in current region
43596 FiniteElement* bnd_ele_pt =
43597 this->boundary_element_in_region_pt(shd_bnd_id, region_id, ier);
43598 // Add the boundary element to the internal boundary in the
43599 // region
43600 this->Boundary_region_element_pt[int_bnd_id][region_id].push_back(
43601 bnd_ele_pt);
43602
43603 // Get the face index of the boundary
43604 int face_index = this->face_index_at_boundary_in_region(
43605 shd_bnd_id, region_id, ier);
43606 // Add the face index to the storage of the boundary region
43607 this->Face_index_region_at_boundary[int_bnd_id][region_id]
43608 .push_back(face_index);
43609
43610 } // for (ier < nele_ir)
43611
43612 } // for (ir < nregions)
43613
43614 } // if (nregions > 1)
43615
43616 } // if (the shared boundary appears in the current processor)
43617
43618 } // for (loop over the shared bound that overlap an internal bound)
43619
43620 // Document the number of elements and nodes associated to the
43621 // boundaries after filling elements and nodes
43622 if (outfile.is_open())
43623 {
43624 const unsigned nbound = this->nboundary();
43625 outfile << "Number of boundaries: " << nbound << "\n\n";
43626 outfile << "Number of elements and nodes associated to each "
43627 << "boundary after\nfilling elements and nodes\n\n";
43628 for (unsigned i = 0; i < nbound; i++)
43629 {
43630 outfile << "Boundary (" << i << ") Elements ("
43631 << this->nboundary_element(i) << ")"
43632 << " Nodes (" << this->nboundary_node(i) << ")\n";
43633 }
43634 }
43635
43636 // ------------------------------------------------------------------
43637 // Finally, re-setup the boundary coordinates for the new nodes on
43638 // the overlaped internal boundaries
43639 // ------------------------------------------------------------------
43640 for (std::set<unsigned>::iterator it = internal_boundary_overlaped.begin();
43641 it != internal_boundary_overlaped.end();
43642 it++)
43643 {
43644 const unsigned overlaped_internal_bnd_id = (*it);
43645
43646 // Re-setup boundary coordinates
43647 this->template setup_boundary_coordinates<ELEMENT>(
43648 overlaped_internal_bnd_id);
43649 }
43650 }
43651
43652#endif // #ifdef OOMPH_HAS_MPI
43653
43654 //======================================================================
43655 /// Move the boundary nodes onto the boundary defined by the old mesh
43656 //======================================================================
43657 template<class ELEMENT>
43659 RefineableTriangleMesh<ELEMENT>*& new_mesh_pt, const unsigned& b)
43660 {
43661 // Quick return
43662 if (!Boundary_coordinate_exists[b])
43663 {
43664 return;
43665 }
43666
43667 // Firstly we set the boundary coordinates of the new nodes
43668 // In case the mapping between the geometric object's intrinsic coordinate
43669 // and the arc-length coordinate is nonlinear. This is only an
43670 // approximation, but it will ensure that the nodes that were input to
43671 // triangle will retain exactly the same boundary coordinates and then
43672 // linear interpolation is used between those values for any newly created
43673 // nodes.
43674
43675 // We need to get the boundary nodes from the boundary face
43676 // elements since the "multi_domain" methods add nodes to the
43677 // "Boundary_node_pt" structure which have no boundary coordinates
43678 // assigned
43679 std::set<Node*> tmp_boundary_node_pt;
43680 const unsigned nboundary_ele = this->nboundary_element(b);
43681 for (unsigned e = 0; e < nboundary_ele; e++)
43682 {
43683 // Get the boundary bulk element
43684 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
43685#ifdef OOMPH_HAS_MPI
43686 // Only work with nonhalo elements if the mesh is distributed
43687 if (!bulk_ele_pt->is_halo())
43688 {
43689#endif
43690 // Get the face index
43691 int face_index = this->face_index_at_boundary(b, e);
43692 // Create the face element
43693 FiniteElement* face_ele_pt =
43694 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
43695
43696 // Get the number of nodes on the face element
43697 const unsigned nnodes = face_ele_pt->nnode();
43698 for (unsigned i = 0; i < nnodes; i++)
43699 {
43700 // Get the nodes in the face elements
43701 Node* tmp_node_pt = face_ele_pt->node_pt(i);
43702 // Add the nodes to the set of boundary nodes
43703 tmp_boundary_node_pt.insert(tmp_node_pt);
43704 } // for (i < nnodes)
43705
43706 // Free the memory allocated for the face element
43707 delete face_ele_pt;
43708 face_ele_pt = 0;
43709#ifdef OOMPH_HAS_MPI
43710 } // if (!bulk_ele_pt->is_halo())
43711#endif
43712
43713 } // for (e < nboundary_ele)
43714
43715 // Get the number of boundary nodes
43716 const unsigned long n_boundary_node = tmp_boundary_node_pt.size();
43717
43718 // Quick return if there are no nodes
43719 if (n_boundary_node == 0)
43720 {
43721#ifdef OOMPH_HAS_MPI
43722 // Check if we are working with a distributed mesh
43723 if (!this->is_mesh_distributed())
43724 {
43725#endif
43726 return;
43727#ifdef OOMPH_HAS_MPI
43728 }
43729 else // The mesh is distributed !!!
43730 {
43731 // Do not forget to participate in the communication
43732 Mesh* face_mesh_pt = new Mesh();
43733 create_unsorted_face_mesh_representation(b, face_mesh_pt);
43734 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(face_mesh_pt);
43735
43736 // Delete the allocated memory for the geometric object and face mesh
43737 delete mesh_geom_obj_pt;
43738
43739 // Flush the nodes from the face mesh to make sure we
43740 // don't delete them (the bulk mesh still needs them!)
43741 face_mesh_pt->flush_node_storage();
43742 delete face_mesh_pt;
43743 return;
43744 }
43745#endif
43746 } // if (n_boundary_node==0)
43747
43748 // Create a vector of existing boundary nodes with their boundary
43749 // coordinate as the first entry so that we can use standard sort algorithms
43750 Vector<double> node_coord(3);
43751 Vector<double> b_coord(1);
43752
43753 Vector<Vector<double>> old_boundary_node(n_boundary_node);
43754 unsigned tmp_counter = 0;
43755 for (std::set<Node*>::iterator it_node = tmp_boundary_node_pt.begin();
43756 it_node != tmp_boundary_node_pt.end();
43757 it_node++, tmp_counter++)
43758 {
43759 Node* nod_pt = (*it_node);
43760 nod_pt->get_coordinates_on_boundary(b, b_coord);
43761 node_coord[0] = b_coord[0];
43762 node_coord[1] = nod_pt->x(0);
43763 node_coord[2] = nod_pt->x(1);
43764 old_boundary_node[tmp_counter] = node_coord;
43765 } // for (it_node != tmp_boundary_node_pt.end())
43766
43767 // Sort the vector
43768 std::sort(old_boundary_node.begin(), old_boundary_node.end());
43769
43770 // Set up an equivalent ordered vector for the new nodes, based on the
43771 // current coordinate which is the scaled arc-length.
43772 // Also provide storage for the original node index,
43773 // the mapped coordinate and a flag to indicate whether the mapped
43774 // coordinate has been assigned.
43775 // Get the nodes on the boundary but consider to which segment (which
43776 // may appear in a distributed mesh) they belong
43777 Vector<Vector<Node*>> segment_nodes_pt;
43778
43779#ifdef OOMPH_HAS_MPI
43780 // Get the number of segments
43781 const unsigned nsegments = new_mesh_pt->nboundary_segment(b);
43782#else
43783 // The number of segments is one since the boundary is not split
43784 // over multiple processors
43785 const unsigned nsegments = 1;
43786#endif // #ifdef OOMPH_HAS_MPI
43787
43788#ifdef OOMPH_HAS_MPI
43789 // Get the total number of nodes on the boundary
43790 const unsigned n_new_boundary_node = new_mesh_pt->nboundary_segment_node(b);
43791
43792 // Check if we are working with a distributed mesh
43793 if (this->is_mesh_distributed())
43794 {
43795 // If that is the case we need to ensure that the new mesh has
43796 // nodes too, if that is not the case then return
43797 // Quick return if there are no nodes
43798 if (n_new_boundary_node == 0)
43799 {
43800 // Do not forget to participate in the communication
43801 Mesh* face_mesh_pt = new Mesh();
43802 create_unsorted_face_mesh_representation(b, face_mesh_pt);
43803 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(face_mesh_pt);
43804
43805 // Delete the allocated memory for the geometric object and face mesh
43806 delete mesh_geom_obj_pt;
43807 // Flush the nodes from the face mesh to make sure we
43808 // don't delete them (the bulk mesh still needs them!)
43809 face_mesh_pt->flush_node_storage();
43810 delete face_mesh_pt;
43811 return;
43812 }
43813 }
43814#endif // #ifdef OOMPH_HAS_MPI
43815
43816 // Create a vector of boundary nodes that must be moved
43817 Vector<Vector<unsigned>> nodes_to_be_snapped(nsegments);
43818
43819 // Go through all the segments to assign the snapped zeta coordinates
43820 // for the new nodes
43821 for (unsigned is = 0; is < nsegments; is++)
43822 {
43823#ifdef OOMPH_HAS_MPI
43824 const unsigned n_new_boundary_segment_node =
43825 new_mesh_pt->nboundary_segment_node(b, is);
43826#else
43827 const unsigned n_new_boundary_segment_node =
43828 new_mesh_pt->nboundary_node(b);
43829#endif // #ifdef OOMPH_HAS_MPI
43830
43831 Vector<Vector<double>> new_boundary_node(n_new_boundary_segment_node);
43832 // There will be six data associated with each node
43833 node_coord.resize(6, 0.0);
43834 for (unsigned n = 0; n < n_new_boundary_segment_node; n++)
43835 {
43836#ifdef OOMPH_HAS_MPI
43837 Node* nod_pt = new_mesh_pt->boundary_segment_node_pt(b, is, n);
43838#else
43839 Node* nod_pt = new_mesh_pt->boundary_node_pt(b, n);
43840#endif // #ifdef OOMPH_HAS_MPI
43841 nod_pt->get_coordinates_on_boundary(b, b_coord);
43842 node_coord[0] = b_coord[0];
43843 node_coord[1] = nod_pt->x(0);
43844 node_coord[2] = nod_pt->x(1);
43845 node_coord[3] = n;
43846 new_boundary_node[n] = node_coord;
43847 } // for (n < n_new_boundary_segment_node)
43848
43849 // Sort the new boundary nodes based on their arc-length coordinate
43850 std::sort(new_boundary_node.begin(), new_boundary_node.end());
43851
43852 // We now have two sets of nodes ordered by a coordinate that acts in the
43853 // same direction and has the same limits.
43854
43855 // Loop over the vector of new nodes and allocate exactly the same
43856 // coordinate as the old nodes at points of coincidence
43857 unsigned old_index = 0;
43858 for (unsigned n = 0; n < n_new_boundary_segment_node; ++n)
43859 {
43860 // Loop over the set of old nodes and if the x and y coordinates
43861 // coincide with the new node copy accross the new boundary coordinate
43862 for (unsigned m = old_index; m < n_boundary_node; ++m)
43863 {
43864 if ((std::fabs(old_boundary_node[m][1] - new_boundary_node[n][1]) <
43865 1.0e-14) &&
43866 (std::fabs(old_boundary_node[m][2] - new_boundary_node[n][2]) <
43867 1.0e-14))
43868 {
43869 // Store the boundary coordinate from the old mesh
43870 new_boundary_node[n][4] = old_boundary_node[m][0];
43871 // Say that it has been stored
43872 new_boundary_node[n][5] = 1.0;
43873 // For efficiency, we can start the iteration from here next
43874 // time round because both vectors are ordered
43875 old_index = m;
43876 break;
43877 }
43878 }
43879 }
43880
43881 // Check that the end-points have new boundary coordinates allocated
43882#ifdef PARANOID
43883 if ((new_boundary_node[0][5] == 0.0) ||
43884 (new_boundary_node[n_new_boundary_segment_node - 1][5] == 0.0))
43885 {
43886 std::ostringstream error_stream;
43887 error_stream
43888 << "New boundary coordinates not found for the first and/or last "
43889 << "nodes\n"
43890 << "on the boundary " << b << ". This should not happen because "
43891 << "these\nlimits should have been setup in the constructor\n";
43892 error_stream
43893 << "The distance between the new and old nodes is probably outside\n"
43894 << "our tolerance.\n";
43895 error_stream.precision(20);
43896 error_stream << "Old boundaries: \n";
43897 error_stream << old_boundary_node[0][1] << " "
43898 << old_boundary_node[0][2] << " : "
43899 << old_boundary_node[n_boundary_node - 1][1] << " "
43900 << old_boundary_node[n_boundary_node - 1][2] << "\n";
43901 error_stream << "New boundaries: \n"
43902 << new_boundary_node[0][1] << " "
43903 << new_boundary_node[0][2] << " : "
43904 << new_boundary_node[n_new_boundary_segment_node - 1][1]
43905 << " "
43906 << new_boundary_node[n_new_boundary_segment_node - 1][2]
43907 << "\n";
43908 OomphLibWarning(error_stream.str(),
43909 "RefineableTriangleMesh::snap_nodes_onto_boundary()",
43910 OOMPH_EXCEPTION_LOCATION);
43911 }
43912#endif
43913
43914 // This is only true if the boundary is not splitted among the
43915 // processors
43916 if (!this->is_mesh_distributed())
43917 {
43918 // The end points should always be present, so we
43919 // can (and must) always add them in exactly
43920 new_boundary_node[0][4] = new_boundary_node[0][0];
43921
43922 /// Correct!? Because assigned again below
43923 new_boundary_node[n_new_boundary_segment_node - 1][4] =
43924 new_boundary_node[0][5] = 1.0;
43925
43926 new_boundary_node[n_new_boundary_segment_node - 1][4] =
43927 new_boundary_node[n_new_boundary_segment_node - 1][0];
43928 new_boundary_node[n_new_boundary_segment_node - 1][5] = 1.0;
43929 }
43930
43931 // Now loop over the interior nodes again and
43932 // use linear interpolation to fill in any unassigned coordiantes
43933 for (unsigned n = 1; n < n_new_boundary_segment_node - 1; ++n)
43934 {
43935 // If the new boundary coordinate has NOT been allocated
43936 if (new_boundary_node[n][5] == 0.0)
43937 {
43938 // Add its (unsorted) node number to the list
43939 nodes_to_be_snapped[is].push_back(
43940 static_cast<unsigned>(new_boundary_node[n][3]));
43941
43942 // We assume that the previous nodal value has been assigned
43943 // and read out the old and new boundary coordinates
43944 double zeta_old_low = new_boundary_node[n - 1][0];
43945 double zeta_new_low = new_boundary_node[n - 1][4];
43946
43947 // Loop over the nodes above the current node until
43948 // we find the next one that has been allocated
43949 for (unsigned m = n + 1; m < n_new_boundary_segment_node; ++m)
43950 {
43951 if (new_boundary_node[m][5] == 1.0)
43952 {
43953 // Read out the old boundary coordinate
43954 double zeta_old_high = new_boundary_node[m][0];
43955 double zeta_new_high = new_boundary_node[m][4];
43956 // Use linear interpolation to assign the new boundary coordinate
43957 double frac = (new_boundary_node[n][0] - zeta_old_low) /
43958 (zeta_old_high - zeta_old_low);
43959 new_boundary_node[n][4] =
43960 zeta_new_low + frac * (zeta_new_high - zeta_new_low);
43961 new_boundary_node[n][5] = 1.0;
43962 break;
43963 }
43964 }
43965 }
43966 }
43967
43968 // Loop over all the nodes and set the new boundary coordinate
43969 for (unsigned n = 0; n < n_new_boundary_segment_node; ++n)
43970 {
43971 if (new_boundary_node[n][5] == 0)
43972 {
43973 throw OomphLibError(
43974 "New boundary coordinate not assigned\n",
43975 "RefineableTriangleMesh::snap_nodes_onto_boundary()",
43976 OOMPH_EXCEPTION_LOCATION);
43977 }
43978
43979#ifdef OOMPH_HAS_MPI
43980 // get the old coordinate
43981 new_mesh_pt
43983 b, is, static_cast<unsigned>(new_boundary_node[n][3]))
43984 ->get_coordinates_on_boundary(b, b_coord);
43985 // Set the new coordinate
43986 b_coord[0] = new_boundary_node[n][4];
43987 new_mesh_pt
43989 b, is, static_cast<unsigned>(new_boundary_node[n][3]))
43990 ->set_coordinates_on_boundary(b, b_coord);
43991#else
43992 // get the old coordinate
43993 new_mesh_pt
43994 ->boundary_node_pt(b, static_cast<unsigned>(new_boundary_node[n][3]))
43995 ->get_coordinates_on_boundary(b, b_coord);
43996 // Set the new coordinate
43997 b_coord[0] = new_boundary_node[n][4];
43998 new_mesh_pt
43999 ->boundary_node_pt(b, static_cast<unsigned>(new_boundary_node[n][3]))
44000 ->set_coordinates_on_boundary(b, b_coord);
44001#endif // #ifdef OOMPH_HAS_MPI
44002 }
44003
44004 } // for (is < nsegments)
44005
44006 Mesh* face_mesh_pt = new Mesh();
44007 create_unsorted_face_mesh_representation(b, face_mesh_pt);
44008
44009 // Now that the coordinates have been set up we can do the snapping
44010 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(face_mesh_pt);
44011
44012 // Now assign the new nodes positions based on the old meshes
44013 // potentially curvilinear boundary (its geom object incarnation)
44014 Vector<double> new_x(2);
44015
44016 // Loop over the nodes that need to be snapped
44017 for (unsigned is = 0; is < nsegments; is++)
44018 {
44019 const unsigned nnodes_to_snap = nodes_to_be_snapped[is].size();
44020
44021 for (unsigned in = 0; in < nnodes_to_snap; in++)
44022 {
44023 // Read out the boundary node number
44024 unsigned n = nodes_to_be_snapped[is][in];
44025#ifdef OOMPH_HAS_MPI
44026 // Get the boundary coordinate of all new nodes
44027 Node* const nod_pt = new_mesh_pt->boundary_segment_node_pt(b, is, n);
44028#else
44029 // Get the boundary coordinate of all new nodes
44030 Node* const nod_pt = new_mesh_pt->boundary_node_pt(b, n);
44031#endif // #ifdef OOMPH_HAS_MPI
44032
44033 nod_pt->get_coordinates_on_boundary(b, b_coord);
44034 // Let's find boundary coordinates of the new node
44035 mesh_geom_obj_pt->position(b_coord, new_x);
44036
44037 // Now snap to the boundary
44038 for (unsigned i = 0; i < 2; i++)
44039 {
44040 nod_pt->x(i) = new_x[i];
44041 }
44042 }
44043 }
44044
44045 // Delete the allocated memory for the geometric object and face mesh
44046 delete mesh_geom_obj_pt;
44047 // Flush the nodes from the face mesh to make sure we
44048 // don't delete them (the bulk mesh still needs them!)
44049 face_mesh_pt->flush_node_storage();
44050 delete face_mesh_pt;
44051
44052 // Fix up the elements adjacent to the boundary
44053
44054 // Dummy six node element for sorting out bubble node for
44055 // seven node enriched quadratic triangles
44056 TElement<2, 3> dummy_six_node_element;
44057 for (unsigned j = 0; j < 6; j++)
44058 {
44059 dummy_six_node_element.construct_node(j);
44060 }
44061
44062 // This should definitely become a triangular element member function
44063 // Loop over elements
44064 unsigned n_bound_el = new_mesh_pt->nboundary_element(b);
44065 for (unsigned e = 0; e < n_bound_el; e++)
44066 {
44067 FiniteElement* el_pt = new_mesh_pt->boundary_element_pt(b, e);
44068
44069 // Deal with different numbers of nodes separately
44070 unsigned nnod = el_pt->nnode();
44071
44072 // #ifdef PARANOID
44073 // // Flag to indicate if we successully classified/dealt with the
44074 // element bool success=false;
44075 // #endif
44076
44077 // Simplex element: Nothing to be done other than error checking
44078 if (nnod == 3)
44079 {
44080#ifdef PARANOID
44081 // Try to cast to a simplex element
44082 TElement<2, 2>* t_el_pt = dynamic_cast<TElement<2, 2>*>(el_pt);
44083 if (t_el_pt == 0)
44084 {
44085 throw OomphLibError(
44086 "Have a three-noded element that's not a TElement<2,2>",
44087 OOMPH_CURRENT_FUNCTION,
44088 OOMPH_EXCEPTION_LOCATION);
44089 }
44090 // If I get there I must not have thrown :)
44091 // success=true;
44092#endif
44093 }
44094 // Quadratic element (or enriched quadratic)
44095
44096 else if ((nnod == 6) || (nnod == 7))
44097 {
44098#ifdef PARANOID
44099 // Try to cast to a quadratic element
44100 TElement<2, 3>* t_el_pt = dynamic_cast<TElement<2, 3>*>(el_pt);
44101 if (t_el_pt == 0)
44102 {
44103 if (nnod == 6)
44104 {
44105 throw OomphLibError(
44106 "Have a six-noded element that's not a TElement<2,3>",
44107 OOMPH_CURRENT_FUNCTION,
44108 OOMPH_EXCEPTION_LOCATION);
44109 }
44110 else
44111 {
44112 throw OomphLibError(
44113 "Have a seven-noded element that's not a TElement<2,3>",
44114 OOMPH_CURRENT_FUNCTION,
44115 OOMPH_EXCEPTION_LOCATION);
44116 }
44117 }
44118 // If I get there I must not have thrown :)
44119 // success=true;
44120#endif
44121 // Deal with six noded stuff for all (normal and enriched) elements
44122
44123 /// ----------------------------------------------------------------
44124 /// Repositioning of mid-side nodes
44125 /// ----------------------------------------------------------------
44126
44127 // Side between 0 and 1
44128 if (el_pt->node_pt(3)->is_on_boundary(b))
44129 {
44130 // Make sure that the node I'm about to move is NOT on
44131 // a boundary
44132 if (!el_pt->node_pt(5)->is_on_boundary())
44133 {
44134 // Reset the internal nodes
44135 for (unsigned i = 0; i < 2; i++)
44136 {
44137 el_pt->node_pt(5)->x(i) =
44138 0.5 * (el_pt->node_pt(0)->x(i) + el_pt->node_pt(2)->x(i));
44139 }
44140 }
44141 // Make sure that the node I'm about to move is NOT on
44142 // a boundary
44143 if (!el_pt->node_pt(4)->is_on_boundary())
44144 {
44145 // Reset the internal nodes
44146 for (unsigned i = 0; i < 2; i++)
44147 {
44148 el_pt->node_pt(4)->x(i) =
44149 0.5 * (el_pt->node_pt(1)->x(i) + el_pt->node_pt(2)->x(i));
44150 }
44151 }
44152 }
44153
44154 // Side between 1 and 2
44155 if (el_pt->node_pt(4)->is_on_boundary(b))
44156 {
44157 // Make sure that the node I'm about to move is NOT on
44158 // a boundary
44159 if (!el_pt->node_pt(5)->is_on_boundary())
44160 {
44161 // Reset the internal nodes
44162 for (unsigned i = 0; i < 2; i++)
44163 {
44164 el_pt->node_pt(5)->x(i) =
44165 0.5 * (el_pt->node_pt(0)->x(i) + el_pt->node_pt(2)->x(i));
44166 }
44167 }
44168 // Make sure that the node I'm about to move is NOT on
44169 // a boundary
44170 if (!el_pt->node_pt(3)->is_on_boundary())
44171 {
44172 // Reset the internal nodes
44173 for (unsigned i = 0; i < 2; i++)
44174 {
44175 el_pt->node_pt(3)->x(i) =
44176 0.5 * (el_pt->node_pt(0)->x(i) + el_pt->node_pt(1)->x(i));
44177 }
44178 }
44179 }
44180
44181 // Side between 0 and 2
44182 if (el_pt->node_pt(5)->is_on_boundary(b))
44183 {
44184 // Make sure that the node I'm about to move is NOT on
44185 // a boundary
44186 if (!el_pt->node_pt(4)->is_on_boundary())
44187 {
44188 // Reset the internal nodes
44189 for (unsigned i = 0; i < 2; i++)
44190 {
44191 el_pt->node_pt(4)->x(i) =
44192 0.5 * (el_pt->node_pt(1)->x(i) + el_pt->node_pt(2)->x(i));
44193 }
44194 }
44195 // Make sure that the node I'm about to move is NOT on
44196 // a boundary
44197 if (!el_pt->node_pt(3)->is_on_boundary())
44198 {
44199 // Reset the internal nodes
44200 for (unsigned i = 0; i < 2; i++)
44201 {
44202 el_pt->node_pt(3)->x(i) =
44203 0.5 * (el_pt->node_pt(0)->x(i) + el_pt->node_pt(1)->x(i));
44204 }
44205 }
44206 }
44207
44208 // If it's seven noded it's likely to be an enriched one: Deal with
44209 // the central (bubble) node
44210 if (nnod == 7)
44211 {
44212 // Try to cast to an enriched quadratic element
44214 dynamic_cast<TBubbleEnrichedElement<2, 3>*>(el_pt);
44215 if (t_el_pt == 0)
44216 {
44217 throw OomphLibError("Have seven-noded element that's not a "
44218 "TBubbleEnrichedElement<2,3>",
44219 OOMPH_CURRENT_FUNCTION,
44220 OOMPH_EXCEPTION_LOCATION);
44221 }
44222
44223 // Assign the new non-bubble coordinates to the six noded dummy
44224 // element
44225 for (unsigned j = 0; j < 6; j++)
44226 {
44227 for (unsigned i = 0; i < 2; i++)
44228 {
44229 dummy_six_node_element.node_pt(j)->x(i) = el_pt->node_pt(j)->x(i);
44230 }
44231 }
44232
44233 // Local coordinate of enriched node
44234 unsigned j_enriched = 6;
44235 Vector<double> s(2);
44236 el_pt->local_coordinate_of_node(j_enriched, s);
44237
44238 // Get its position from non-enriched element
44239 Vector<double> x(2);
44240 dummy_six_node_element.interpolated_x(s, x);
44241 el_pt->node_pt(j_enriched)->x(0) = x[0];
44242 el_pt->node_pt(j_enriched)->x(1) = x[1];
44243 }
44244 }
44245 // Any other case cannot be dealt with at the moment
44246
44247 else
44248 {
44249 std::ostringstream error_stream;
44250 error_stream << "Cannot deal with this particular " << nnod
44251 << "-noded element yet.\n"
44252 << "Please implement this yourself.\n";
44253 throw OomphLibError(
44254 error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
44255 }
44256 }
44257
44258 // Cleanup
44259 for (unsigned j = 0; j < 6; j++)
44260 {
44261 delete dummy_six_node_element.node_pt(j);
44262 }
44263 }
44264
44265
44266#endif // #ifdef OOMPH_HAS_TRIANGLE_LIB
44267
44268} // namespace oomph
44269
44270#endif
e
Definition: cfortran.h:571
static char t char * s
Definition: cfortran.h:568
cstr elem_len * i
Definition: cfortran.h:603
char t
Definition: cfortran.h:568
///////////////////////////////////////////////////////////////////// ///////////////////////////////...
///////////////////////////////////////////////////////////////////////////// ///////////////////////...
void get_bin(const Vector< double > &zeta, int &bin_number)
Get the number of the bin containing the specified coordinate. Bin number is negative if the coordina...
void fill_bin_by_diffusion(const unsigned &bin_diffusion_radius=1)
Fill bin by diffusion, populating each empty bin with the same content as the first non-empty bin fou...
const std::map< unsigned, Vector< std::pair< FiniteElement *, Vector< double > > > > * get_all_bins_content() const
Get the contents of all bins in vector.
void get_fill_stats(unsigned &n_bin, unsigned &max_n_entry, unsigned &min_n_entry, unsigned &tot_n_entry, unsigned &n_empty) const
Provide some stats on the fill level of the associated bin.
////////////////////////////////////////////////////////////////////
virtual void update_node_update(AlgebraicNode *&node_pt)=0
Update the node update info for given node, following mesh adaptation. Must be implemented for every ...
GeomObject * geom_object_list_pt(const unsigned &i)
Access function to the ith GeomObject.
unsigned ngeom_object_list_pt()
Return number of geometric objects associated with AlgebraicMesh.
////////////////////////////////////////////////////////////////////
unsigned ngeom_object(const int &id)
Number of geometric objects involved in id-th update function.
unsigned nref_value(const int &id)
Number of reference values involved in id-th update function.
GeomObject * geom_object_pt(const unsigned &i)
Return pointer to i-th geometric object involved in default (usually first) update function.
int node_update_fct_id()
Default (usually first if there are multiple ones) node update fct id.
double ref_value(const unsigned &i)
Return i-th reference value involved in default (usually first) update function.
void add_node_update_info(const int &id, AlgebraicMesh *mesh_pt, const Vector< GeomObject * > &geom_object_pt, const Vector< double > &ref_value, const bool &called_from_constructor=false)
Add algebraic update information for node: What's the ID of the mesh update function (typically used ...
Vector< unsigned > & dimensions_of_bin_array()
Number of bins in each coordinate direction.
A class that contains the information required by Nodes that are located on Mesh boundaries....
Definition: nodes.h:1997
std::map< unsigned, unsigned > *& index_of_first_value_assigned_by_face_element_pt()
Return pointer to the map giving the index of the first face element value.
Definition: nodes.h:2047
/////////////////////////////////////////////////////////////////////////// /////////////////////////...
A class that represents a collection of data; each Data object may contain many different individual ...
Definition: nodes.h:86
void set_value(const unsigned &i, const double &value_)
Set the i-th stored data value to specified value. The only reason that we require an explicit set fu...
Definition: nodes.h:271
unsigned nvalue() const
Return number of values stored in data object (incl pinned ones).
Definition: nodes.h:483
double value(const unsigned &i) const
Return i-th stored value. This function is not virtual so that it can be inlined. This means that if ...
Definition: nodes.h:293
Information for documentation of results: Directory and file number to enable output in the form RESL...
void disable_doc()
Disable documentation.
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
Definition: elements.h:5042
Class that is used to create FaceElement from bulk elements and to provide these FaceElement with a g...
void set_boundary_number_in_bulk_mesh(const unsigned &b)
Set function for the boundary number in bulk mesh.
Definition: elements.h:4510
A general Finite Element class.
Definition: elements.h:1313
virtual void local_coordinate_of_node(const unsigned &j, Vector< double > &s) const
Get local coordinates of node j in the element; vector sets its own size (broken virtual)
Definition: elements.h:1847
virtual double interpolated_x(const Vector< double > &s, const unsigned &i) const
Return FE interpolated coordinate x[i] at local coordinate s.
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
virtual Node * construct_node(const unsigned &n)
Construct the local node n and return a pointer to the newly created node object.
Definition: elements.h:2514
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2215
virtual Node * construct_boundary_node(const unsigned &n)
Construct the local node n as a boundary node; that is a node that MAY be placed on a mesh boundary a...
Definition: elements.h:2543
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2180
A Generalised Element class.
Definition: elements.h:73
bool is_halo() const
Is this element a halo?
Definition: elements.h:1163
int non_halo_proc_ID()
ID of processor ID that holds non-halo counterpart of halo element; negative if not a halo.
Definition: elements.h:1170
bool must_be_kept_as_halo() const
Test whether the element must be kept as a halo element.
Definition: elements.h:1189
Generalised timestepper that can serve a variety of purposes in continuation, bifurcation detection a...
/////////////////////////////////////////////////////////////////////
Definition: geom_objects.h:101
unsigned ndim() const
Access function to # of Eulerian coordinates.
Definition: geom_objects.h:177
MacroElementNodeUpdateMeshes contain MacroElementNodeUpdateNodes which have their own node update fun...
Vector< GeomObject * > geom_object_vector_pt()
Access function to the vector of GeomObject.
////////////////////////////////////////////////////////////////////
void set_node_update_info(FiniteElement *node_update_element_pt, const Vector< double > &s_in_node_update_element, const Vector< GeomObject * > &geom_object_pt)
Set node update information for node: Pass the pointer to the element that performs the update operat...
///////////////////////////////////////////////////////////////////// ///////////////////////////////...
SamplePointContainer * sample_point_container_pt() const
Pointer to the sample point container.
void position(const Vector< double > &zeta, Vector< double > &r) const
Return the position as a function of the intrinsic coordinate zeta. This provides an (expensive!...
A general mesh class.
Definition: mesh.h:67
unsigned long nboundary_node(const unsigned &ibound) const
Return number of nodes on a particular boundary.
Definition: mesh.h:833
void flush_element_and_node_storage()
Flush storage for elements and nodes by emptying the vectors that store the pointers to them....
Definition: mesh.h:407
FiniteElement * finite_element_pt(const unsigned &e) const
Upcast (downcast?) to FiniteElement (needed to access FiniteElement member functions).
Definition: mesh.h:473
int face_index_at_boundary(const unsigned &b, const unsigned &e) const
For the e-th finite element on boundary b, return int to indicate the face_index of the face adjacent...
Definition: mesh.h:896
unsigned nboundary_element(const unsigned &b) const
Return number of finite elements that are adjacent to boundary b.
Definition: mesh.h:878
Node *& node_pt(const unsigned long &n)
Return pointer to global node n.
Definition: mesh.h:436
void set_nodal_and_elemental_time_stepper(TimeStepper *const &time_stepper_pt, const bool &preserve_existing_data)
Set the timestepper associated with all nodal and elemental data stored in the mesh.
Definition: mesh.h:1032
FiniteElement * boundary_element_pt(const unsigned &b, const unsigned &e) const
Return pointer to e-th finite element on boundary b.
Definition: mesh.h:840
unsigned nboundary() const
Return number of boundaries.
Definition: mesh.h:827
unsigned long nnode() const
Return number of nodes in the mesh.
Definition: mesh.h:596
GeneralisedElement *& element_pt(const unsigned long &e)
Return pointer to element e.
Definition: mesh.h:448
void add_element_pt(GeneralisedElement *const &element_pt)
Add a (pointer to) an element to the mesh.
Definition: mesh.h:617
void flush_node_storage()
Flush storage for nodes (only) by emptying the vectors that store the pointers to them.
Definition: mesh.h:430
unsigned long nelement() const
Return number of elements in the mesh.
Definition: mesh.h:590
Node *& boundary_node_pt(const unsigned &b, const unsigned &n)
Return pointer to node n on boundary b.
Definition: mesh.h:493
/////////////////////////////////////////////////////////////////////////// /////////////////////////...
An oomph-lib wrapper to the MPI_Comm communicator object. Just contains an MPI_Comm object (which is ...
Definition: communicator.h:54
An OomphLibError object which should be thrown when an run-time error is encountered....
An OomphLibWarning object which should be created as a temporary object to issue a warning....
void enable_problem_distributed()
Enable problem distributed.
Definition: problem.h:971
void add_time_stepper_pt(TimeStepper *const &time_stepper_pt)
Add a timestepper to the problem. The function will automatically create or resize the Time object so...
Definition: problem.cc:1545
Mesh *& mesh_pt()
Return a pointer to the global mesh.
Definition: problem.h:1280
Projection problem. This is created during the adaptation of unstructured meshes and it is assumed th...
Definition: projection.h:695
void disable_use_iterative_solver_for_projection()
Disbales the use of an iterative solver for projection.
Definition: projection.h:742
void project(Mesh *base_mesh_pt, const bool &dont_project_positions=false)
Project from base into the problem's own mesh.
Definition: projection.h:748
///////////////////////////////////////////////////////////////////// ///////////////////////////////...
/////////////////////////////////////////////////////////////////// /////////////////////////////////...
void construct_new_halo_node_helper(Node *&new_nod_pt, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, unsigned &iproc, unsigned &node_index, FiniteElement *const &new_el_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function which constructs a new halo node (on an element) with the information sent from the h...
void add_received_node_load_balance_helper(Node *&new_nod_pt, Vector< Vector< FiniteElement * > > &f_haloed_ele_pt, Vector< Vector< std::map< unsigned, FiniteElement * > > > &received_old_haloed_element_pt, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, unsigned &iproc, unsigned &node_index, FiniteElement *const &new_el_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function to add a new node from load balance.
bool update_shared_curve_using_elements_area(Vector< TriangleMeshPolyLine * > &vector_polyline_pt, const Vector< double > &target_areas)
Updates the polylines using the elements area as constraint for the number of points along the bounda...
bool get_connected_vertex_number_on_dst_boundary(Vector< double > &vertex_coordinates, const unsigned &dst_b_id, unsigned &vertex_number)
Computes the associated vertex number on the destination boundary.
void send_boundary_node_info_of_shared_nodes(Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Get the original boundaries to which is associated each shared node, and send the info....
void update_shared_curve_after_restart(Vector< TriangleMeshPolyLine * > &vector_polyline_pt)
Updates the shared polylines representation after restart.
void send_and_receive_elements_nodes_info(int &send_proc, int &recv_proc)
Helper function to send back halo and haloed information.
void add_halo_node_helper(Node *&new_nod_pt, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, unsigned &iproc, unsigned &node_index, FiniteElement *const &new_el_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function to add halo node.
double & max_element_size()
Max element size allowed during adaptation.
void snap_nodes_onto_boundary(RefineableTriangleMesh< ELEMENT > *&new_mesh_pt, const unsigned &b)
Snap the boundary nodes onto any curvilinear boundaries.
void reset_shared_boundary_elements_and_nodes(const bool flush_elements=true, const bool update_elements=true, const bool flush_nodes=true, const bool update_nodes=true)
Re-establish the shared boundary elements after the adaptation process (the updating of shared nodes ...
bool unrefine_shared_boundary_constrained_by_target_area(const unsigned &b, const unsigned &c, Vector< Vector< double > > &vector_bnd_vertices, Vector< double > &area_constraint)
Helper function that performs the unrefinement process on the specified boundary by using the provide...
void get_boundary_segment_nodes_helper(const unsigned &b, Vector< Vector< Node * > > &tmp_segment_nodes)
Get the nodes on the boundary (b), these are stored in the segment they belong (also used by the load...
bool unrefine_boundary(const unsigned &b, const unsigned &c, Vector< Vector< double > > &vector_bnd_vertices, double &unrefinement_tolerance, const bool &check_only=false)
Helper function that performs the unrefinement process.
void get_face_mesh_representation(TriangleMeshPolygon *polygon_pt, Vector< Mesh * > &face_mesh_pt)
Helper function to construct face mesh representation of all polylines, possibly with segments re-dis...
void add_non_delete_vertices_from_boundary_helper(Vector< Vector< Node * > > src_bound_segment_node_pt, Vector< Vector< Node * > > dst_bound_segment_node_pt, const unsigned &dst_bnd_id, const unsigned &dst_bnd_chunk)
Adds the vertices from the sources boundary that are repeated in the destination boundary to the list...
void add_node_load_balance_helper(unsigned &iproc, Vector< Vector< FiniteElement * > > &f_halo_ele_pt, Vector< Node * > &new_nodes_on_domain, Node *nod_pt)
Helper function to add haloed node.
void compute_shared_node_degree_helper(Vector< Vector< FiniteElement * > > &unsorted_face_ele_pt, std::map< Node *, unsigned > &global_node_degree)
Computes the degree of the nodes on the shared boundaries, the degree of the node is computed from th...
void resume_boundary_connections(Vector< TriangleMeshPolyLine * > &resume_initial_connection_polyline_pt, Vector< TriangleMeshPolyLine * > &resume_final_connection_polyline_pt)
Resume the boundary connections that may have been suspended because the destination boundary is no p...
void get_required_elemental_information_load_balance_helper(unsigned &iproc, Vector< Vector< FiniteElement * > > &f_haloed_ele_pt, FiniteElement *ele_pt)
Helper function to get the required elemental information from the element to be sent....
void sort_nodes_on_shared_boundaries()
Sort the nodes on shared boundaries so that the processors that share a boundary agree with the order...
double & min_element_size()
Min element size allowed during adaptation.
bool update_open_curve_using_elements_area(TriangleMeshOpenCurve *&open_curve_pt, const Vector< double > &target_area)
Updates the open curve but using the elements area instead of the default refinement and unrefinement...
void construct_new_node_load_balance_helper(Node *&new_nod_pt, Vector< Vector< FiniteElement * > > &f_haloed_ele_pt, Vector< Vector< std::map< unsigned, FiniteElement * > > > &received_old_haloed_element_pt, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, unsigned &iproc, unsigned &node_index, FiniteElement *const &new_el_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function which constructs a new node (on an element) with the information sent from the load b...
void create_halo_element(unsigned &iproc, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function to create (halo) elements on the loop process based on the info received in send_and_...
void get_shared_boundary_elements_and_face_indexes(const Vector< FiniteElement * > &first_element_pt, const Vector< FiniteElement * > &second_element_pt, Vector< FiniteElement * > &first_shared_boundary_element_pt, Vector< unsigned > &first_shared_boundary_element_face_index, Vector< FiniteElement * > &second_shared_boundary_element_pt, Vector< unsigned > &second_shared_boundary_element_face_index)
Use the first and second group of elements to find the intersection between them to get the shared bo...
void create_sorted_face_mesh_representation(const unsigned &boundary_id, Mesh *face_mesh_pt, std::map< FiniteElement *, bool > &is_inverted, bool &inverted_face_mesh)
Helper function Creates a sorted face mesh representation of the specified PolyLine It means that the...
void update_open_curve_after_restart(TriangleMeshOpenCurve *&open_curve_pt)
Updates the open curve representation after restart.
void reset_halo_haloed_scheme()
In charge of. re-establish the halo(ed) scheme on all processors. Sends info. to create halo elements...
bool refine_boundary(Mesh *face_mesh_pt, Vector< Vector< double > > &vector_bnd_vertices, double &refinement_tolerance, const bool &check_only=false)
Helper function that performs the refinement process on the specified boundary by using the provided ...
void create_element_load_balance_helper(unsigned &iproc, Vector< Vector< FiniteElement * > > &f_haloed_ele_pt, Vector< Vector< std::map< unsigned, FiniteElement * > > > &received_old_haloed_element_pt, Vector< FiniteElement * > &new_elements_on_domain, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function to create elements on the loop process based on the info received in send_and_receive...
void create_new_shared_boundaries(std::set< FiniteElement * > &element_in_processor_pt, Vector< Vector< FiniteElement * > > &new_shared_boundary_element_pt, Vector< Vector< unsigned > > &new_shared_boundary_element_face_index)
Creates the new shared boundaries, this method is also in charge of computing the shared boundaries i...
void get_shared_boundary_segment_nodes_helper(const unsigned &shd_bnd_id, Vector< Vector< Node * > > &tmp_segment_nodes)
Get the nodes on the shared boundary (b), these are stored in the segment they belong.
void refine_triangulateio(TriangulateIO &triangulate_io, const Vector< double > &target_area, TriangulateIO &triangle_refine)
Build a new TriangulateIO object from previous TriangulateIO based on target area for each element.
void load_balance(const Vector< unsigned > &input_target_domain_for_local_non_halo_element)
Performs the load balancing for unstructured meshes, the load balancing strategy is based on mesh mig...
void create_unsorted_face_mesh_representation(const unsigned &boundary_id, Mesh *face_mesh_pt)
Helper function Creates an unsorted face mesh representation from the specified boundary id....
void add_element_load_balance_helper(const unsigned &iproc, Vector< Vector< std::map< unsigned, FiniteElement * > > > &received_old_haloed_element_pt, FiniteElement *ele_pt)
Helper function to create elements on the loop process based on the info received in send_and_receive...
void restore_boundary_connections(Vector< TriangleMeshPolyLine * > &resume_initial_connection_polyline_pt, Vector< TriangleMeshPolyLine * > &resume_final_connection_polyline_pt)
After unrefinement and refinement has taken place compute the new vertices numbers of the boundaries ...
bool update_open_curve_using_face_mesh(TriangleMeshOpenCurve *open_polyline_pt, const bool &check_only=false)
Helper function that updates the input open curve by using end-points of elements from FaceMesh(es) t...
bool apply_max_length_constraint(Mesh *face_mesh_pt, Vector< Vector< double > > &vector_bnd_vertices, double &max_length_constraint)
bool refine_boundary_constrained_by_target_area(MeshAsGeomObject *mesh_geom_obj_pt, Vector< Vector< double > > &vector_bnd_vertices, double &refinement_tolerance, Vector< double > &area_constraint)
Helper function that performs the refinement process on the specified boundary by using the provided ...
void create_temporary_boundary_connections(Vector< TriangleMeshPolygon * > &tmp_outer_polygons_pt, Vector< TriangleMeshOpenCurve * > &tmp_open_curves_pt)
After unrefinement and refinement has taken place compute the new vertices numbers of the temporary r...
void add_haloed_node_helper(unsigned &iproc, Node *nod_pt)
Helper function to add haloed node.
void get_required_nodal_information_helper(unsigned &iproc, Node *nod_pt)
Helper function to get the required nodal information from a haloed node so that a fully-functional h...
void compute_global_node_names_and_shared_nodes(Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Compute the names of the nodes on shared boundaries in this (my_rank) processor with other processors...
double & min_permitted_angle()
Min angle before remesh gets triggered.
void reset_halo_haloed_scheme_helper(Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< Vector< Node * > > &iproc_currently_created_nodes_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
In charge of creating additional halo(ed) elements on those processors that have no shared boundaries...
void get_required_nodal_information_load_balance_helper(Vector< Vector< FiniteElement * > > &f_halo_ele_pt, unsigned &iproc, Node *nod_pt)
Helper function to get the required nodal information from an haloed node so that a fully-functional ...
bool unrefine_boundary_constrained_by_target_area(const unsigned &b, const unsigned &c, Vector< Vector< double > > &vector_bnd_vertices, double &unrefinement_tolerance, Vector< double > &area_constraint)
Helper function that performs the unrefinement process on the specified boundary by using the provide...
void add_halo_element_helper(unsigned &iproc, FiniteElement *ele_pt)
Helper function to create (halo) elements on the loop process based on the info received in send_and_...
void update_other_proc_shd_bnd_node_helper(Node *&new_nod_pt, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< unsigned > &other_processor_1, Vector< unsigned > &other_processor_2, Vector< unsigned > &other_shared_boundaries, Vector< unsigned > &other_indexes, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function that assigns/updates the references to the node so that it can be found with any othe...
void create_adjacency_matrix_new_shared_edges_helper(Vector< Vector< FiniteElement * > > &unsorted_face_ele_pt, Vector< Vector< Node * > > &tmp_sorted_shared_node_pt, std::map< Node *, Vector< Vector< unsigned > > > &node_alias, Vector< Vector< Vector< unsigned > > > &adjacency_matrix)
Sort the nodes on the new shared boundaries (after load balancing), computes the alias of the nodes a...
bool refine_shared_boundary_constrained_by_target_area(Vector< Vector< double > > &vector_bnd_vertices, Vector< double > &area_constraint)
Helper function that performs the refinement process on the specified boundary by using the provided ...
bool update_polygon_using_face_mesh(TriangleMeshPolygon *polygon_pt, const bool &check_only=false)
Helper function that updates the input polygon's PSLG by using the end-points of elements from FaceMe...
void update_polygon_after_restart(TriangleMeshPolygon *&polygon_pt)
Updates the polylines representation after restart.
void add_vertices_for_non_deletion()
Mark the vertices that are not allowed for deletion by the unrefienment/refinement polyline methods....
void create_polylines_from_polyfiles(const std::string &node_file_name, const std::string &poly_file_name)
Helper function to create polylines and fill associate data.
bool update_polygon_using_elements_area(TriangleMeshPolygon *&polygon_pt, const Vector< double > &target_area)
Updates the polylines using the elements area as constraint for the number of points along the bounda...
void restore_polyline_connections_helper(TriangleMeshPolyLine *polyline_pt, Vector< TriangleMeshPolyLine * > &resume_initial_connection_polyline_pt, Vector< TriangleMeshPolyLine * > &resume_final_connection_polyline_pt)
Restore the connections of the specific polyline The vertices numbering on the destination boundaries...
void adapt(const Vector< double > &elem_error)
Adapt mesh, based on elemental error provided.
void enable_use_eulerian_coordinates_during_setup()
Enable use of eulerian coordinates (via interpolated_x) during setup (otherwise use interpolated_zeta...
General SolidMesh class.
Definition: mesh.h:2562
A Class for nodes that deform elastically (i.e. position is an unknown in the problem)....
Definition: nodes.h:1687
void add_values_to_vector(Vector< double > &vector_of_values)
Add all data, position and time history values to the vector Overload to add the Lagrangian coordinat...
Definition: nodes.cc:3708
Data *const & variable_position_pt() const
Pointer to variable_position data (const version)
Definition: nodes.h:1766
void read_values_from_vector(const Vector< double > &vector_of_values, unsigned &index)
Read all data and time history values from the vector starting from index. On return the index will b...
Definition: nodes.cc:3755
////////////////////////////////////////////////////////////////////// //////////////////////////////...
Definition: timesteppers.h:231
unsigned ntstorage() const
Return the number of doubles required to represent history (one for steady)
Definition: timesteppers.h:601
Class to keep track of discrete/continous time. It is essential to have a single Time object when usi...
Definition: timesteppers.h:63
TriangulateIO & triangulateio_representation()
Access to the triangulateio representation of the mesh.
bool is_internal_point_fixed() const
Test whether the internal point is fixed.
Vector< double > internal_point() const
Coordinates of the internal point.
Base class for defining a triangle mesh boundary, this class has the methods that allow to connect th...
virtual unsigned boundary_chunk() const =0
Boundary chunk (Used when a boundary is represented by more than one polyline.
double unrefinement_tolerance()
Get tolerance for unrefinement of curve section to create a better representation of curvilinear boun...
void set_unrefinement_tolerance(const double &tolerance)
Set tolerance for unrefinement of curve sections to avoid unnecessarily large numbers of elements on ...
virtual unsigned boundary_id() const =0
Boundary id.
void connect_initial_vertex_to_polyline(TriangleMeshPolyLine *polyline_pt, const unsigned &vertex_number, const double &tolerance_for_connection=1.0e-14)
Connects the initial vertex of the curve section to a desired target polyline by specifying the verte...
void resume_initial_vertex_connected()
Resumes the initial vertex connection, it may be that after load balancing the boundary to which the ...
void suspend_final_vertex_connected()
Set the final vertex connection as suspended, it will be resumed when the method to resume the connec...
unsigned initial_vertex_connected_n_vertex() const
Gets the vertex number to which the initial end is connected.
void resume_final_vertex_connected()
Resumes the final vertex connection, it may be that after load balancing the boundary to which the co...
void set_refinement_tolerance(const double &tolerance)
Set tolerance for refinement of curve sections to create a better representation of curvilinear bound...
virtual unsigned nvertex() const =0
Number of vertices.
void connect_final_vertex_to_polyline(TriangleMeshPolyLine *polyline_pt, const unsigned &vertex_number, const double &tolerance_for_connection=1.0e-14)
Connects the final vertex of the curve section to a desired target polyline by specifying the vertex ...
unsigned final_vertex_connected_n_chunk() const
Gets the boundary chunk to which the final end is connected.
void set_maximum_length(const double &maximum_length)
Allows to specify the maximum distance between two vertices that define the associated polyline of th...
unsigned final_vertex_connected_n_vertex() const
Sets the vertex number to which the final end is connected.
double refinement_tolerance()
Get tolerance for refinement of curve sections to create a better representation of curvilinear bound...
bool is_final_vertex_connected() const
Test whether final vertex is connected or not.
unsigned initial_vertex_connected_bnd_id() const
Gets the id to which the initial end is connected.
bool is_initial_vertex_connected() const
Test whether initial vertex is connected or not.
void set_initial_vertex_connected()
Sets the initial vertex as connected.
void set_final_vertex_connected()
Sets the final vertex as connected.
void suspend_initial_vertex_connected()
Set the initial vertex connection as suspended, it will be resumed when the method to resume the conn...
unsigned initial_vertex_connected_n_chunk() const
Gets the boundary chunk to which the initial end is connected.
double maximum_length()
Gets access to the maximum length variable.
unsigned final_vertex_connected_bnd_id() const
Gets the id to which the final end is connected.
virtual TriangleMeshCurveSection * curve_section_pt(const unsigned &i) const
Pointer to i-th constituent curve section.
virtual unsigned ncurve_section() const
Number of constituent curves.
////////////////////////////////////////////////////////////////////// //////////////////////////////...
TriangleMeshPolyLine * polyline_pt(const unsigned &i) const
Pointer to i-th constituent polyline.
///////////////////////////////////////////////////////////////////// ///////////////////////////////...
void enable_use_attributes()
Helper function for enabling the use of attributes.
double element_area() const
Helper function for getting the element area.
void disable_automatic_creation_of_vertices_on_boundaries()
Disables the creation of points (by Triangle) on the outer and internal boundaries.
std::map< unsigned, Vector< double > > & regions_coordinates()
Helper function for getting access to the regions coordinates.
Vector< TriangleMeshClosedCurve * > internal_closed_curve_pt() const
Helper function for getting the internal closed boundaries.
Vector< Vector< double > > extra_holes_coordinates() const
Helper function for getting the extra holes.
void set_communicator_pt(OomphCommunicator *comm_pt)
Function to set communicator (mesh is then assumed to be distributed)
Vector< TriangleMeshOpenCurve * > internal_open_curves_pt() const
Helper function for getting the internal open boundaries.
Class defining a polyline for use in Triangle Mesh generation.
Vector< double > vertex_coordinate(const unsigned &i) const
Coordinate vector of i-th vertex (const version)
unsigned boundary_chunk() const
Boundary chunk (Used when a boundary is represented by more than one polyline.
void final_vertex_coordinate(Vector< double > &vertex)
Get last vertex coordinates.
unsigned nvertex() const
Number of vertices.
void reverse()
Reverse the polyline, this includes the connection information and the vertices order.
void initial_vertex_coordinate(Vector< double > &vertex)
Get first vertex coordinates.
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
unsigned npolyline() const
Number of constituent polylines.
bool can_update_reference_configuration() const
Test whether curve can update reference.
TriangleMeshPolyLine * polyline_pt(const unsigned &i) const
Pointer to i-th constituent polyline.
bool is_redistribution_of_segments_between_polylines_enabled()
Is re-distribution of polyline segments in the curve between different boundaries during adaptation e...
virtual void reset_reference_configuration()
Virtual function that should be overloaded to update the polygons reference configuration.
///////////////////////////////////////////////////////////////////// ///////////////////////////////...
const int check_connections_of_polyline_nodes(std::set< FiniteElement * > &element_in_processor_pt, const int &root_edge_bnd_id, std::map< std::pair< Node *, Node * >, bool > &overlapped_face, std::map< unsigned, std::map< Node *, bool > > &node_on_bnd_not_overlapped_by_shd_bnd, std::list< Node * > &current_polyline_nodes, std::map< unsigned, std::list< Node * > > &shared_bnd_id_to_sorted_list_node_pt, const unsigned &node_degree, Node *&new_node_pt, const bool called_from_load_balance=false)
Check for any possible connections that the array of sorted nodes have with any previous boundaries o...
void compute_boundary_segments_connectivity_and_initial_zeta_values(const unsigned &b)
Compute the boundary segments connectivity for those boundaries that were splited during the distribu...
void break_loops_on_shared_polyline_helper(const unsigned &initial_shd_bnd_id, std::list< Node * > &input_nodes, Vector< FiniteElement * > &input_boundary_element_pt, Vector< int > &input_face_index_element, const int &input_connect_to_the_left, const int &input_connect_to_the_right, Vector< std::list< Node * > > &output_sorted_nodes_pt, Vector< Vector< FiniteElement * > > &output_boundary_element_pt, Vector< Vector< int > > &output_face_index_element, Vector< int > &output_connect_to_the_left, Vector< int > &output_connect_to_the_right)
Break any possible loop created by the sorted list of nodes that is used to create a new shared polyl...
void build_triangulateio(const std::string &poly_file_name, TriangulateIO &triangulate_io, bool &use_attributes)
Helper function to create TriangulateIO object (return in triangulate_io) from the ....
void create_shared_boundaries(OomphCommunicator *comm_pt, const Vector< unsigned > &element_domain, const Vector< GeneralisedElement * > &backed_up_el_pt, const Vector< FiniteElement * > &backed_up_f_el_pt, std::map< Data *, std::set< unsigned > > &processors_associated_with_data, const bool &overrule_keep_as_halo_element_status)
Creates the shared boundaries.
Vector< unsigned > oomph_vertex_nodes_id()
Return the vector that contains the oomph-lib node number for all vertex nodes in the TriangulateIO r...
void create_shared_polylines_connections()
Establish the connections of the polylines previously marked as having connections....
void break_loops_on_shared_polyline_load_balance_helper(const unsigned &initial_shd_bnd_id, std::list< Node * > &input_nodes, Vector< FiniteElement * > &input_boundary_element_pt, Vector< FiniteElement * > &input_boundary_face_element_pt, Vector< int > &input_face_index_element, const int &input_connect_to_the_left, const int &input_connect_to_the_right, Vector< std::list< Node * > > &output_sorted_nodes_pt, Vector< Vector< FiniteElement * > > &output_boundary_element_pt, Vector< Vector< FiniteElement * > > &output_boundary_face_element_pt, Vector< Vector< int > > &output_face_index_element, Vector< int > &output_connect_to_the_left, Vector< int > &output_connect_to_the_right)
Break any possible loop created by the sorted list of nodes that is used to create a new shared polyl...
void synchronize_boundary_coordinates(const unsigned &b)
In charge of sinchronize the boundary coordinates for internal boundaries that were split as part of ...
void compute_holes_left_by_halo_elements_helper(Vector< Vector< double > > &output_holes_coordinates)
Compute the holes left by the halo elements, those adjacent to the shared boundaries.
void identify_boundary_segments_and_assign_initial_zeta_values(const unsigned &b, Vector< FiniteElement * > &input_face_ele_pt, const bool &is_internal_boundary, std::map< FiniteElement *, FiniteElement * > &face_to_bulk_element_pt)
Identify the segments from the old mesh (original mesh) in the new mesh (this) and assign initial and...
virtual void reset_boundary_element_info(Vector< unsigned > &ntmp_boundary_elements, Vector< Vector< unsigned > > &ntmp_boundary_elements_in_region, Vector< FiniteElement * > &deleted_elements)
Virtual function to perform the reset boundary elements info routines. Generally used after load bala...
const bool shared_boundary_overlaps_internal_boundary(const unsigned &shd_bnd_id)
Checks if the shared boundary overlaps an internal boundary.
Vector< Vector< Node * > > & boundary_segment_node_pt(const unsigned &b)
Return direct access to nodes associated with a boundary but sorted in segments.
Vector< Vector< Vector< unsigned > > > shared_boundaries_ids() const
void re_scale_re_assigned_initial_zeta_values_for_internal_boundary(const unsigned &b)
Re-scale the re-assigned zeta values for the boundary nodes, apply only for internal boundaries.
void create_shared_polyline(const unsigned &my_rank, const unsigned &shd_bnd_id, const unsigned &iproc, const unsigned &jproc, std::list< Node * > &sorted_nodes, const int &root_edge_bnd_id, Vector< FiniteElement * > &bulk_bnd_ele_pt, Vector< int > &face_index_ele, Vector< Vector< TriangleMeshPolyLine * > > &unsorted_polylines_pt, const int &connect_to_the_left_flag, const int &connect_to_the_right_flag)
Create the shared polyline and fill the data structured that keep all the information associated with...
void update_holes_information_helper(Vector< TriangleMeshPolygon * > &polygons_pt, Vector< Vector< double > > &output_holes_coordinates)
Keeps those vertices that define a hole, those that are inside closed internal boundaries in the new ...
void create_distributed_domain_representation(Vector< TriangleMeshPolygon * > &polygons_pt, Vector< TriangleMeshOpenCurve * > &open_curves_pt)
Creates the distributed domain representation. Joins the original boundaires, shared boundaries and c...
void build_from_scaffold(TimeStepper *time_stepper_pt, const bool &use_attributes)
Build mesh from scaffold.
void sort_polylines_helper(Vector< TriangleMeshPolyLine * > &unsorted_polylines_pt, Vector< Vector< TriangleMeshPolyLine * > > &sorted_polylines_pt)
Sorts the polylines so they be continuous and then we can create a closed or open curve from them.
void create_tmp_open_curves_helper(Vector< Vector< TriangleMeshPolyLine * > > &sorted_open_curves_pt, Vector< TriangleMeshPolyLine * > &unsorted_shared_to_internal_poly_pt, Vector< TriangleMeshOpenCurve * > &open_curves_pt)
Take the polylines from the original open curves and created new temporaly representations of open cu...
void get_halo_elements_on_all_procs(const unsigned &nproc, const Vector< unsigned > &element_domain, const Vector< GeneralisedElement * > &backed_up_el_pt, std::map< Data *, std::set< unsigned > > &processors_associated_with_data, const bool &overrule_keep_as_halo_element_status, std::map< GeneralisedElement *, unsigned > &element_to_global_index, Vector< Vector< Vector< GeneralisedElement * > > > &output_halo_elements_pt)
Creates the halo elements on all processors Gets the halo elements on all processors,...
void dump_distributed_info_for_restart(std::ostream &dump_file)
Used to dump info. related with distributed triangle meshes.
void re_assign_initial_zeta_values_for_internal_boundary(const unsigned &b, Vector< std::list< FiniteElement * > > &old_segment_sorted_ele_pt, std::map< FiniteElement *, bool > &old_is_inverted)
Re-assign the boundary segments initial zeta (arclength) value for those internal boundaries that wer...
void set_mesh_level_time_stepper(TimeStepper *const &time_stepper_pt, const bool &preserve_existing_data)
Overload set_mesh_level_time_stepper so that the stored time stepper now corresponds to the new times...
void read_distributed_info_for_restart(std::istream &restart_file)
Used to read info. related with distributed triangle meshes.
void select_boundary_face_elements(Vector< FiniteElement * > &face_el_pt, const unsigned &b, bool &is_internal_boundary, std::map< FiniteElement *, FiniteElement * > &face_to_bulk_element_pt)
Select face element from boundary using the criteria to decide which of the two face elements should ...
void create_polylines_from_halo_elements_helper(const Vector< unsigned > &element_domain, std::map< GeneralisedElement *, unsigned > &element_to_global_index, std::set< FiniteElement * > &element_in_processor_pt, Vector< Vector< Vector< GeneralisedElement * > > > &input_halo_elements, std::map< std::pair< Node *, Node * >, unsigned > &elements_edges_on_boundary, Vector< Vector< Vector< TriangleMeshPolyLine * > > > &output_polylines_pt)
Creates polylines from the intersection of halo elements on all processors. The new polylines define ...
void output_boundary_coordinates(const unsigned &b, std::ostream &outfile)
Output the nodes on the boundary and their respective boundary coordinates(into separate tecplot zone...
void create_tmp_polygons_helper(Vector< Vector< TriangleMeshPolyLine * > > &polylines_pt, Vector< TriangleMeshPolygon * > &polygons_pt)
Take the polylines from the shared boundaries and create temporary polygon representations of the dom...
void get_element_edges_on_boundary(std::map< std::pair< Node *, Node * >, unsigned > &element_edges_on_boundary)
Get the element edges (pair of nodes, edges) that lie on a boundary (used to mark shared boundaries t...
std::map< unsigned, Vector< double > > & boundary_segment_final_arclength()
Return direct access to the final arclength for the segments that are part of a boundary.
std::map< unsigned, Vector< double > > & boundary_segment_initial_zeta()
Return direct access to the initial zeta for the segments that are part of a boundary.
GeomObject * boundary_geom_object_pt(const unsigned &b)
Return the geometric object associated with the b-th boundary or null if the boundary has associated ...
double region_attribute(const unsigned &i)
Return the attribute associated with region i.
FiniteElement * boundary_element_in_region_pt(const unsigned &b, const unsigned &r, const unsigned &e) const
Return pointer to the e-th element adjacent to boundary b in region r.
std::map< unsigned, Vector< double > > & boundary_final_coordinate()
Return direct access to the final coordinates of a boundary.
unsigned nboundary_segment(const unsigned &b)
Return the number of segments associated with a boundary.
std::map< unsigned, Vector< Vector< double > > > & boundary_segment_final_coordinate()
Return direct access to the final coordinates for the segments that are part of a boundary.
unsigned long nboundary_segment_node(const unsigned &b)
Return the number of segments associated with a boundary.
std::map< unsigned, Vector< Vector< double > > > & boundary_segment_initial_coordinate()
Return direct access to the initial coordinates for the segments that are part of a boundary.
int face_index_at_boundary_in_region(const unsigned &b, const unsigned &r, const unsigned &e) const
Return face index of the e-th element adjacent to boundary b in region r.
std::map< unsigned, Vector< double > > & boundary_initial_coordinate()
Return direct access to the initial coordinates of a boundary.
unsigned nboundary_element_in_region(const unsigned &b, const unsigned &r) const
Return the number of elements adjacent to boundary b in region r.
unsigned nregion()
Return the number of regions specified by attributes.
std::map< unsigned, Vector< double > > & boundary_segment_final_zeta()
Return direct access to the final zeta for the segments that are part of a boundary.
std::map< unsigned, Vector< double > > & boundary_segment_initial_arclength()
Return direct access to the initial arclength for the segments that are part of a boundary.
FiniteElement * region_element_pt(const unsigned &i, const unsigned &e)
Return the e-th element in the i-th region.
std::map< unsigned, Vector< double > > & boundary_initial_zeta_coordinate()
Return direct access to the initial zeta coordinate of a boundary.
unsigned nregion_element(const unsigned &i)
Return the number of elements in the i-th region.
std::map< unsigned, Vector< double > > & boundary_coordinate_limits()
Return access to the vector of boundary coordinates associated with each geometric object.
std::map< unsigned, Vector< double > > & boundary_final_zeta_coordinate()
Return direct access to the final zeta coordinates of a boundary.
A slight extension to the standard template vector class so that we can include "graceful" array rang...
Definition: Vector.h:58
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn't been defined.
bool Doc_comprehensive_timings
Global boolean to switch on comprehensive timing – can probably be declared const false when developm...
void get_required_nodal_information_helper(int &iproc, Node *nod_pt, Mesh *const &mesh_pt, int &n_cont_inter_values, Vector< unsigned > &send_unsigneds, Vector< double > &send_doubles)
Helper function to get the required nodal information from an external haloed node so that a fully-fu...
Vector< std::string > Flat_packed_unsigneds_string
unsigned Counter_for_flat_packed_unsigneds
Counter used when processing vector of flat-packed unsigneds – this is really "private" data,...
Vector< double > Flat_packed_doubles
Vector of flat-packed doubles to be communicated with other processors.
Vector< unsigned > Flat_packed_unsigneds
Vector of flat-packed unsigneds to be communicated with other processors – this is really "private" d...
bool Doc_timings
Boolean to indicate whether to doc timings or not.
bool Doc_stats
Boolean to indicate whether to output basic info during setup_multi_domain_interaction() routines.
bool Doc_full_stats
Boolean to indicate whether to output further info during setup_multi_domain_interaction() routines.
unsigned Counter_for_flat_packed_doubles
Counter used when processing vector of flat-packed doubles – this is really "private" data,...
double timer()
returns the time in seconds after some point in past
double Tolerable_error
Acceptable discrepancy for mismatch in vertex coordinates. In paranoid mode, the code will die if the...
void initialise_triangulateio(TriangulateIO &triangle_io)
Initialise TriangulateIO structure.
void clear_triangulateio(TriangulateIO &triangulate_io, const bool &clear_hole_data)
Clear TriangulateIO structure.
TriangulateIO deep_copy_of_triangulateio_representation(TriangulateIO &triangle_io, const bool &quiet)
Make (partial) deep copy of TriangulateIO object. We only copy those items we need within oomph-lib's...
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
frac
A class for all elements that solve the Advection Diffusion equations using isoparametric elements.
x
Pseudo buckling ring: Circular ring deformed by the N-th buckling mode of a thin-wall elastic ring.
u
Z2-error-estimator: Elements that can be used with Z2 error estimation should be derived from the bas...
struct oomph::classcomp Bottom_left_sorter
int f(x_0, x_1...)\ dx_0 \ dx_1...
Generic class for numerical integration schemes:
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...
The Triangle data structure, modified from the triangle.h header supplied with triangle 1....
double * pointlist
Pointer to list of points x coordinate followed by y coordinate.
int * pointmarkerlist
Pointer to list of point markers.
double * pointattributelist
Pointer to list of point attributes.
bool operator()(const std::pair< double, double > &lhs, const std::pair< double, double > &rhs) const